1 /*
2 * Copyright 2016, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "RSSPIRVWriter.h"
18
19 #include "Builtin.h"
20 #include "Context.h"
21 #include "GlobalAllocPass.h"
22 #include "GlobalAllocSPIRITPass.h"
23 #include "GlobalMergePass.h"
24 #include "InlinePreparationPass.h"
25 #include "RemoveNonkernelsPass.h"
26 #include "SPIRVModule.h"
27 #include "Wrapper.h"
28 #include "bcinfo/MetadataExtractor.h"
29 #include "pass_queue.h"
30
31 #include "llvm/ADT/Triple.h"
32 #include "llvm/IR/LegacyPassManager.h"
33 #include "llvm/IR/Module.h"
34 #include "llvm/Support/CommandLine.h"
35 #include "llvm/Support/Debug.h"
36 #include "llvm/Support/SPIRV.h"
37 #include "llvm/Support/raw_ostream.h"
38 #include "llvm/Transforms/IPO.h"
39 #include "llvm/Transforms/Scalar.h"
40
41 #define DEBUG_TYPE "rs2spirv-writer"
42
43 using namespace llvm;
44 using namespace SPIRV;
45
46 namespace rs2spirv {
47
48 namespace {
49
HandleTargetTriple(llvm::Module & M)50 void HandleTargetTriple(llvm::Module &M) {
51 Triple TT(M.getTargetTriple());
52 auto Arch = TT.getArch();
53
54 StringRef NewTriple;
55 switch (Arch) {
56 default:
57 llvm_unreachable("Unrecognized architecture");
58 break;
59 case Triple::arm:
60 NewTriple = "spir-unknown-unknown";
61 break;
62 case Triple::aarch64:
63 NewTriple = "spir64-unknown-unknown";
64 break;
65 case Triple::spir:
66 case Triple::spir64:
67 DEBUG(dbgs() << "!!! Already a spir triple !!!\n");
68 }
69
70 DEBUG(dbgs() << "New triple:\t" << NewTriple << "\n");
71 M.setTargetTriple(NewTriple);
72 }
73
74 } // namespace
75
addPassesForRS2SPIRV(llvm::legacy::PassManager & PassMgr)76 void addPassesForRS2SPIRV(llvm::legacy::PassManager &PassMgr) {
77 PassMgr.add(createInlinePreparationPass());
78 PassMgr.add(createAlwaysInlinerPass());
79 PassMgr.add(createRemoveNonkernelsPass());
80 // Delete unreachable globals.
81 PassMgr.add(createGlobalDCEPass());
82 // Remove dead debug info.
83 PassMgr.add(createStripDeadDebugInfoPass());
84 // Remove dead func decls.
85 PassMgr.add(createStripDeadPrototypesPass());
86 PassMgr.add(createGlobalMergePass());
87 // Transform global allocations and accessors (rs[GS]etElementAt)
88 PassMgr.add(createGlobalAllocPass());
89 // Removed dead MemCpys in 64-bit targets after global alloc pass
90 PassMgr.add(createDeadStoreEliminationPass());
91 PassMgr.add(createAggressiveDCEPass());
92 // Delete unreachable globals (after removing global allocations)
93 PassMgr.add(createRemoveAllGlobalAllocPass());
94 PassMgr.add(createPromoteMemoryToRegisterPass());
95 PassMgr.add(createTransOCLMD());
96 // TODO: investigate removal of OCLTypeToSPIRV pass.
97 PassMgr.add(createOCLTypeToSPIRV());
98 PassMgr.add(createSPIRVRegularizeLLVM());
99 PassMgr.add(createSPIRVLowerConstExpr());
100 PassMgr.add(createSPIRVLowerBool());
101 }
102
WriteSPIRV(llvm::Module * M,std::unique_ptr<bcinfo::MetadataExtractor> ME,llvm::raw_ostream & OS,std::string & ErrMsg)103 bool WriteSPIRV(llvm::Module *M, std::unique_ptr<bcinfo::MetadataExtractor> ME,
104 llvm::raw_ostream &OS, std::string &ErrMsg) {
105 HandleTargetTriple(*M);
106
107 Context &Ctxt = Context::getInstance();
108
109 if (!Ctxt.Initialize(std::move(ME))) {
110 ErrMsg = "Failed to intialize rs2spirv";
111 return false;
112 }
113
114 llvm::legacy::PassManager PassMgr;
115 addPassesForRS2SPIRV(PassMgr);
116
117 std::unique_ptr<SPIRVModule> BM(SPIRVModule::createSPIRVModule());
118
119 PassMgr.add(createLLVMToSPIRV(BM.get()));
120 PassMgr.run(*M);
121 DEBUG(M->dump());
122
123 if (BM->getError(ErrMsg) != SPIRVEC_Success) {
124 return false;
125 }
126
127 llvm::SmallString<4096> O;
128 llvm::raw_svector_ostream SVOS(O);
129
130 SVOS << *BM;
131
132 llvm::StringRef str = SVOS.str();
133 std::vector<uint32_t> words(str.size() / 4);
134
135 memcpy(words.data(), str.data(), str.size());
136
137 android::spirit::PassQueue spiritPasses;
138 spiritPasses.append(CreateWrapperPass(*M));
139 spiritPasses.append(CreateBuiltinPass());
140 spiritPasses.append(CreateGAPass());
141
142 int error;
143 auto wordsOut = spiritPasses.run(words, &error);
144
145 if (error != 0) {
146 OS << *BM;
147 ErrMsg = "Failed to generate wrappers for kernels";
148 return false;
149 }
150
151 OS.write(reinterpret_cast<const char *>(wordsOut.data()),
152 wordsOut.size() * 4);
153
154 return true;
155 }
156
157 } // namespace rs2spirv
158