19
19
#include " llvm/ADT/SetVector.h"
20
20
#include " llvm/ADT/Triple.h"
21
21
#include " llvm/Bitcode/BitcodeWriterPass.h"
22
+ #include " llvm/GenXIntrinsics/GenXSPIRVWriterAdaptor.h"
22
23
#include " llvm/IR/IRPrintingPasses.h"
23
24
#include " llvm/IR/InstIterator.h"
24
25
#include " llvm/IR/Instructions.h"
25
26
#include " llvm/IR/LLVMContext.h"
26
27
#include " llvm/IR/LegacyPassManager.h"
27
28
#include " llvm/IR/Module.h"
28
29
#include " llvm/IRReader/IRReader.h"
30
+ #include " llvm/SYCLLowerIR/LowerESIMD.h"
29
31
#include " llvm/Support/CommandLine.h"
30
32
#include " llvm/Support/InitLLVM.h"
31
33
#include " llvm/Support/Path.h"
35
37
#include " llvm/Support/WithColor.h"
36
38
#include " llvm/Transforms/IPO.h"
37
39
#include " llvm/Transforms/IPO/GlobalDCE.h"
40
+ #include " llvm/Transforms/InstCombine/InstCombine.h"
41
+ #include " llvm/Transforms/Scalar.h"
38
42
#include " llvm/Transforms/Utils/Cloning.h"
39
43
40
44
#include < memory>
@@ -92,6 +96,45 @@ static cl::opt<bool> SplitEsimd{"split-esimd",
92
96
cl::desc (" Split SYCL and ESIMD kernels" ),
93
97
cl::cat (PostLinkCat)};
94
98
99
+ // TODO Design note: sycl-post-link should probably separate different kinds of
100
+ // its functionality on logical and source level:
101
+ // - LLVM IR module splitting
102
+ // - Running LLVM IR passes on resulting modules
103
+ // - Generating additional files (like spec constants, dead arg info,...)
104
+ // The tool itself could be just a "driver" creating needed pipelines from the
105
+ // above actions. This could help make the tool structure clearer and more
106
+ // maintainable.
107
+
108
+ static cl::opt<bool > LowerEsimd{
109
+ " lower-esimd" , cl::desc (" Lower ESIMD constructs" ), cl::cat (PostLinkCat)};
110
+
111
+ static cl::opt<bool >
112
+ OptLevelO0 (" O0" , cl::desc(" Optimization level 0. Similar to clang -O0" ),
113
+ cl::cat(PostLinkCat));
114
+
115
+ static cl::opt<bool >
116
+ OptLevelO1 (" O1" , cl::desc(" Optimization level 1. Similar to clang -O1" ),
117
+ cl::cat(PostLinkCat));
118
+
119
+ static cl::opt<bool >
120
+ OptLevelO2 (" O2" , cl::desc(" Optimization level 2. Similar to clang -O2" ),
121
+ cl::cat(PostLinkCat));
122
+
123
+ static cl::opt<bool > OptLevelOs (
124
+ " Os" ,
125
+ cl::desc (
126
+ " Like -O2 with extra optimizations for size. Similar to clang -Os" ),
127
+ cl::cat(PostLinkCat));
128
+
129
+ static cl::opt<bool > OptLevelOz (
130
+ " Oz" ,
131
+ cl::desc (" Like -Os but reduces code size further. Similar to clang -Oz" ),
132
+ cl::cat(PostLinkCat));
133
+
134
+ static cl::opt<bool >
135
+ OptLevelO3 (" O3" , cl::desc(" Optimization level 3. Similar to clang -O3" ),
136
+ cl::cat(PostLinkCat));
137
+
95
138
enum IRSplitMode {
96
139
SPLIT_PER_TU, // one module per translation unit
97
140
SPLIT_PER_KERNEL, // one module per kernel
@@ -625,6 +668,59 @@ static string_vector saveResultSymbolsLists(string_vector &ResSymbolsLists,
625
668
} \
626
669
}
627
670
671
+ // Helper function for creating Inliner pass.
672
+ // The approach is taken from opt tool.
673
+ static Pass *createFunctionInliningPassHelper () {
674
+ if (OptLevelO0)
675
+ return createFunctionInliningPass (0 , 0 , false );
676
+
677
+ if (OptLevelO1)
678
+ return createFunctionInliningPass (1 , 0 , false );
679
+
680
+ if (OptLevelO2)
681
+ return createFunctionInliningPass (2 , 0 , false );
682
+
683
+ if (OptLevelOs)
684
+ return createFunctionInliningPass (2 , 1 , false );
685
+
686
+ if (OptLevelOz)
687
+ return createFunctionInliningPass (2 , 2 , false );
688
+
689
+ if (OptLevelO3)
690
+ return createFunctionInliningPass (3 , 0 , false );
691
+
692
+ return createFunctionInliningPass ();
693
+ }
694
+
695
+ // When ESIMD code was separated from the regular SYCL code,
696
+ // we can safely process ESIMD part.
697
+ // TODO: support options like -debug-pass, -print-[before|after], and others
698
+ static void LowerEsimdConstructs (Module &M) {
699
+ legacy::PassManager MPM;
700
+ MPM.add (createSYCLLowerESIMDPass ());
701
+ if (!OptLevelO0) {
702
+ // Inlining and SROA passes are required to make
703
+ // ESIMD/accessor_gather_scatter.cpp test work.
704
+ MPM.add (createFunctionInliningPassHelper ());
705
+ MPM.add (createSROAPass ());
706
+ }
707
+ MPM.add (createESIMDLowerVecArgPass ());
708
+ MPM.add (createESIMDLowerLoadStorePass ());
709
+ if (!OptLevelO0) {
710
+ MPM.add (createSROAPass ());
711
+ MPM.add (createEarlyCSEPass (true ));
712
+ MPM.add (createInstructionCombiningPass ());
713
+ MPM.add (createDeadCodeEliminationPass ());
714
+ MPM.add (createFunctionInliningPassHelper ());
715
+ MPM.add (createSROAPass ());
716
+ MPM.add (createEarlyCSEPass (true ));
717
+ MPM.add (createInstructionCombiningPass ());
718
+ MPM.add (createDeadCodeEliminationPass ());
719
+ }
720
+ MPM.add (createGenXSPIRVWriterAdaptorPass ());
721
+ MPM.run (M);
722
+ }
723
+
628
724
using TableFiles = std::map<StringRef, string_vector>;
629
725
630
726
static TableFiles processOneModule (std::unique_ptr<Module> M, bool IsEsimd,
@@ -633,6 +729,9 @@ static TableFiles processOneModule(std::unique_ptr<Module> M, bool IsEsimd,
633
729
if (!M)
634
730
return TblFiles;
635
731
732
+ if (IsEsimd && LowerEsimd)
733
+ LowerEsimdConstructs (*M);
734
+
636
735
std::map<StringRef, std::vector<Function *>> GlobalsSet;
637
736
638
737
bool DoSplit = SplitMode.getNumOccurrences () > 0 ;
@@ -685,11 +784,16 @@ static TableFiles processOneModule(std::unique_ptr<Module> M, bool IsEsimd,
685
784
ResultModules.push_back (std::move (M));
686
785
687
786
{
688
- // reuse input module if there were no spec constants and no splitting
787
+ // Reuse input module with only regular SYCL kernels if there were
788
+ // no spec constants and no splitting.
789
+ // We cannot reuse input module for ESIMD code since it was transformed.
790
+ bool CanReuseInputModule = !SpecConstsMet && (ResultModules.size () == 1 ) &&
791
+ !SyclAndEsimdKernels && !IsEsimd;
689
792
string_vector Files =
690
- SpecConstsMet || (ResultModules.size () > 1 ) || SyclAndEsimdKernels
691
- ? saveResultModules (ResultModules, IsEsimd ? " esimd_" : " " )
692
- : string_vector{InputFilename};
793
+ CanReuseInputModule
794
+ ? string_vector{InputFilename}
795
+ : saveResultModules (ResultModules, IsEsimd ? " esimd_" : " " );
796
+
693
797
// "Code" column is always output
694
798
std::copy (Files.begin (), Files.end (),
695
799
std::back_inserter (TblFiles[COL_CODE]));
@@ -816,6 +920,10 @@ int main(int argc, char **argv) {
816
920
" - Specialization constant intrinsic transformer. Replaces symbolic\n "
817
921
" ID-based intrinsics to integer ID-based ones to make them friendly\n "
818
922
" for the SPIRV translator\n "
923
+ " When the tool splits input module into regular SYCL and ESIMD kernels,\n "
924
+ " it performs a set of specific lowering and transformation passes on\n "
925
+ " ESIMD module, which is enabled by the '-lower-esimd' option. Regular\n "
926
+ " optimization level options are supported, e.g. -O[0|1|2|3|s|z].\n "
819
927
" Normally, the tool generates a number of files and \" file table\"\n "
820
928
" file listing all generated files in a table manner. For example, if\n "
821
929
" the input file 'example.bc' contains two kernels, then the command\n "
0 commit comments