1 /*
2  * Copyright 2012, 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 "bcc/Renderscript/RSCompilerDriver.h"
18 
19 #include "llvm/IR/AssemblyAnnotationWriter.h"
20 #include <llvm/IR/Module.h>
21 #include "llvm/Linker/Linker.h"
22 #include <llvm/Support/CommandLine.h>
23 #include <llvm/Support/Path.h>
24 #include <llvm/Support/raw_ostream.h>
25 
26 #include "bcinfo/BitcodeWrapper.h"
27 #include "bcc/Assert.h"
28 #include "bcinfo/MetadataExtractor.h"
29 #include "bcc/BCCContext.h"
30 #include "bcc/Compiler.h"
31 #include "bcc/Config/Config.h"
32 #include "bcc/Renderscript/RSScript.h"
33 #include "bcc/Renderscript/RSScriptGroupFusion.h"
34 #include "bcc/Support/CompilerConfig.h"
35 #include "bcc/Source.h"
36 #include "bcc/Support/FileMutex.h"
37 #include "bcc/Support/Log.h"
38 #include "bcc/Support/InputFile.h"
39 #include "bcc/Support/Initialization.h"
40 #include "bcc/Support/OutputFile.h"
41 
42 #include <sstream>
43 #include <string>
44 
45 #ifdef HAVE_ANDROID_OS
46 #include <cutils/properties.h>
47 #endif
48 #include <utils/StopWatch.h>
49 
50 using namespace bcc;
51 
RSCompilerDriver(bool pUseCompilerRT)52 RSCompilerDriver::RSCompilerDriver(bool pUseCompilerRT) :
53     mConfig(nullptr), mCompiler(), mDebugContext(false),
54     mLinkRuntimeCallback(nullptr), mEnableGlobalMerge(true),
55     mEmbedGlobalInfo(false), mEmbedGlobalInfoSkipConstant(false) {
56   init::Initialize();
57 }
58 
~RSCompilerDriver()59 RSCompilerDriver::~RSCompilerDriver() {
60   delete mConfig;
61 }
62 
63 
64 #if defined(PROVIDE_ARM_CODEGEN)
65 extern llvm::cl::opt<bool> EnableGlobalMerge;
66 #endif
67 
setupConfig(const RSScript & pScript)68 bool RSCompilerDriver::setupConfig(const RSScript &pScript) {
69   bool changed = false;
70 
71   const llvm::CodeGenOpt::Level script_opt_level =
72       static_cast<llvm::CodeGenOpt::Level>(pScript.getOptimizationLevel());
73 
74 #if defined(PROVIDE_ARM_CODEGEN)
75   EnableGlobalMerge = mEnableGlobalMerge;
76 #endif
77 
78   if (mConfig != nullptr) {
79     // Renderscript bitcode may have their optimization flag configuration
80     // different than the previous run of RS compilation.
81     if (mConfig->getOptimizationLevel() != script_opt_level) {
82       mConfig->setOptimizationLevel(script_opt_level);
83       changed = true;
84     }
85   } else {
86     // Haven't run the compiler ever.
87     mConfig = new (std::nothrow) CompilerConfig(DEFAULT_TARGET_TRIPLE_STRING);
88     if (mConfig == nullptr) {
89       // Return false since mConfig remains NULL and out-of-memory.
90       return false;
91     }
92     mConfig->setOptimizationLevel(script_opt_level);
93     changed = true;
94   }
95 
96 #if defined(PROVIDE_ARM_CODEGEN)
97   bcinfo::MetadataExtractor me(&pScript.getSource().getModule());
98   if (!me.extract()) {
99     assert("Could not extract RS pragma metadata for module!");
100   }
101 
102   bool script_full_prec = (me.getRSFloatPrecision() == bcinfo::RS_FP_Full);
103   if (mConfig->getFullPrecision() != script_full_prec) {
104     mConfig->setFullPrecision(script_full_prec);
105     changed = true;
106   }
107 #endif
108 
109   return changed;
110 }
111 
compileScript(RSScript & pScript,const char * pScriptName,const char * pOutputPath,const char * pRuntimePath,const char * pBuildChecksum,bool pDumpIR)112 Compiler::ErrorCode RSCompilerDriver::compileScript(RSScript& pScript, const char* pScriptName,
113                                                     const char* pOutputPath,
114                                                     const char* pRuntimePath,
115                                                     const char* pBuildChecksum,
116                                                     bool pDumpIR) {
117   // embed build checksum metadata into the source
118   if (pBuildChecksum != nullptr && strlen(pBuildChecksum) > 0) {
119     pScript.getSource().addBuildChecksumMetadata(pBuildChecksum);
120   }
121 
122   // Verify that the only external functions in pScript are Renderscript
123   // functions.  Fail if verification returns an error.
124   if (mCompiler.screenGlobalFunctions(pScript) != Compiler::kSuccess) {
125     return Compiler::kErrInvalidSource;
126   }
127 
128   //===--------------------------------------------------------------------===//
129   // Link RS script with Renderscript runtime.
130   //===--------------------------------------------------------------------===//
131   if (!RSScript::LinkRuntime(pScript, pRuntimePath)) {
132     ALOGE("Failed to link script '%s' with Renderscript runtime %s!",
133           pScriptName, pRuntimePath);
134     return Compiler::kErrInvalidSource;
135   }
136 
137   {
138     // FIXME(srhines): Windows compilation can't use locking like this, but
139     // we also don't need to worry about concurrent writers of the same file.
140 #ifndef USE_MINGW
141     //===------------------------------------------------------------------===//
142     // Acquire the write lock for writing output object file.
143     //===------------------------------------------------------------------===//
144     FileMutex<FileBase::kWriteLock> write_output_mutex(pOutputPath);
145 
146     if (write_output_mutex.hasError() || !write_output_mutex.lock()) {
147       ALOGE("Unable to acquire the lock for writing %s! (%s)",
148             pOutputPath, write_output_mutex.getErrorMessage().c_str());
149       return Compiler::kErrInvalidSource;
150     }
151 #endif
152 
153     // Open the output file for write.
154     OutputFile output_file(pOutputPath,
155                            FileBase::kTruncate | FileBase::kBinary);
156 
157     if (output_file.hasError()) {
158         ALOGE("Unable to open %s for write! (%s)", pOutputPath,
159               output_file.getErrorMessage().c_str());
160       return Compiler::kErrInvalidSource;
161     }
162 
163     // Setup the config to the compiler.
164     bool compiler_need_reconfigure = setupConfig(pScript);
165 
166     if (mConfig == nullptr) {
167       ALOGE("Failed to setup config for RS compiler to compile %s!",
168             pOutputPath);
169       return Compiler::kErrInvalidSource;
170     }
171 
172     if (compiler_need_reconfigure) {
173       Compiler::ErrorCode err = mCompiler.config(*mConfig);
174       if (err != Compiler::kSuccess) {
175         ALOGE("Failed to config the RS compiler for %s! (%s)",pOutputPath,
176               Compiler::GetErrorString(err));
177         return Compiler::kErrInvalidSource;
178       }
179     }
180 
181     OutputFile *ir_file = nullptr;
182     llvm::raw_fd_ostream *IRStream = nullptr;
183     if (pDumpIR) {
184       std::string path(pOutputPath);
185       path.append(".ll");
186       ir_file = new OutputFile(path.c_str(), FileBase::kTruncate);
187       IRStream = ir_file->dup();
188     }
189 
190     // Run the compiler.
191     Compiler::ErrorCode compile_result =
192         mCompiler.compile(pScript, output_file, IRStream);
193 
194     if (ir_file) {
195       ir_file->close();
196       delete ir_file;
197     }
198 
199     if (compile_result != Compiler::kSuccess) {
200       ALOGE("Unable to compile the source to file %s! (%s)", pOutputPath,
201             Compiler::GetErrorString(compile_result));
202       return Compiler::kErrInvalidSource;
203     }
204   }
205 
206   return Compiler::kSuccess;
207 }
208 
build(BCCContext & pContext,const char * pCacheDir,const char * pResName,const char * pBitcode,size_t pBitcodeSize,const char * pBuildChecksum,const char * pRuntimePath,RSLinkRuntimeCallback pLinkRuntimeCallback,bool pDumpIR)209 bool RSCompilerDriver::build(BCCContext &pContext,
210                              const char *pCacheDir,
211                              const char *pResName,
212                              const char *pBitcode,
213                              size_t pBitcodeSize,
214                              const char *pBuildChecksum,
215                              const char *pRuntimePath,
216                              RSLinkRuntimeCallback pLinkRuntimeCallback,
217                              bool pDumpIR) {
218     //  android::StopWatch build_time("bcc: RSCompilerDriver::build time");
219   //===--------------------------------------------------------------------===//
220   // Check parameters.
221   //===--------------------------------------------------------------------===//
222   if ((pCacheDir == nullptr) || (pResName == nullptr)) {
223     ALOGE("Invalid parameter passed to RSCompilerDriver::build()! (cache dir: "
224           "%s, resource name: %s)", ((pCacheDir) ? pCacheDir : "(null)"),
225                                     ((pResName) ? pResName : "(null)"));
226     return false;
227   }
228 
229   if ((pBitcode == nullptr) || (pBitcodeSize <= 0)) {
230     ALOGE("No bitcode supplied! (bitcode: %p, size of bitcode: %u)",
231           pBitcode, static_cast<unsigned>(pBitcodeSize));
232     return false;
233   }
234 
235   //===--------------------------------------------------------------------===//
236   // Construct output path.
237   // {pCacheDir}/{pResName}.o
238   //===--------------------------------------------------------------------===//
239   llvm::SmallString<80> output_path(pCacheDir);
240   llvm::sys::path::append(output_path, pResName);
241   llvm::sys::path::replace_extension(output_path, ".o");
242 
243   //===--------------------------------------------------------------------===//
244   // Load the bitcode and create script.
245   //===--------------------------------------------------------------------===//
246   Source *source = Source::CreateFromBuffer(pContext, pResName,
247                                             pBitcode, pBitcodeSize);
248   if (source == nullptr) {
249     return false;
250   }
251 
252   RSScript script(*source);
253   if (pLinkRuntimeCallback) {
254     setLinkRuntimeCallback(pLinkRuntimeCallback);
255   }
256 
257   script.setLinkRuntimeCallback(getLinkRuntimeCallback());
258 
259   script.setEmbedGlobalInfo(mEmbedGlobalInfo);
260   script.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant);
261 
262   // Read information from bitcode wrapper.
263   bcinfo::BitcodeWrapper wrapper(pBitcode, pBitcodeSize);
264   script.setCompilerVersion(wrapper.getCompilerVersion());
265   script.setOptimizationLevel(static_cast<RSScript::OptimizationLevel>(
266                               wrapper.getOptimizationLevel()));
267 
268   //===--------------------------------------------------------------------===//
269   // Compile the script
270   //===--------------------------------------------------------------------===//
271   Compiler::ErrorCode status = compileScript(script, pResName,
272                                              output_path.c_str(),
273                                              pRuntimePath,
274                                              pBuildChecksum,
275                                              pDumpIR);
276 
277   return status == Compiler::kSuccess;
278 }
279 
buildScriptGroup(BCCContext & Context,const char * pOutputFilepath,const char * pRuntimePath,const char * pRuntimeRelaxedPath,bool dumpIR,const char * buildChecksum,const std::vector<Source * > & sources,const std::list<std::list<std::pair<int,int>>> & toFuse,const std::list<std::string> & fused,const std::list<std::list<std::pair<int,int>>> & invokes,const std::list<std::string> & invokeBatchNames)280 bool RSCompilerDriver::buildScriptGroup(
281     BCCContext& Context, const char* pOutputFilepath, const char* pRuntimePath,
282     const char* pRuntimeRelaxedPath, bool dumpIR, const char* buildChecksum,
283     const std::vector<Source*>& sources,
284     const std::list<std::list<std::pair<int, int>>>& toFuse,
285     const std::list<std::string>& fused,
286     const std::list<std::list<std::pair<int, int>>>& invokes,
287     const std::list<std::string>& invokeBatchNames) {
288   // ---------------------------------------------------------------------------
289   // Link all input modules into a single module
290   // ---------------------------------------------------------------------------
291 
292   llvm::LLVMContext& context = Context.getLLVMContext();
293   llvm::Module module("Merged Script Group", context);
294 
295   llvm::Linker linker(&module);
296   for (Source* source : sources) {
297     if (linker.linkInModule(&source->getModule())) {
298       ALOGE("Linking for module in source failed.");
299       return false;
300     }
301   }
302 
303   // ---------------------------------------------------------------------------
304   // Create fused kernels
305   // ---------------------------------------------------------------------------
306 
307   auto inputIter = toFuse.begin();
308   for (const std::string& nameOfFused : fused) {
309     auto inputKernels = *inputIter++;
310     std::vector<Source*> sourcesToFuse;
311     std::vector<int> slots;
312 
313     for (auto p : inputKernels) {
314       sourcesToFuse.push_back(sources[p.first]);
315       slots.push_back(p.second);
316     }
317 
318     if (!fuseKernels(Context, sourcesToFuse, slots, nameOfFused, &module)) {
319       return false;
320     }
321   }
322 
323   // ---------------------------------------------------------------------------
324   // Rename invokes
325   // ---------------------------------------------------------------------------
326 
327   auto invokeIter = invokes.begin();
328   for (const std::string& newName : invokeBatchNames) {
329     auto inputInvoke = *invokeIter++;
330     auto p = inputInvoke.front();
331     Source* source = sources[p.first];
332     int slot = p.second;
333 
334     if (!renameInvoke(Context, source, slot, newName, &module)) {
335       return false;
336     }
337   }
338 
339   // ---------------------------------------------------------------------------
340   // Compile the new module with fused kernels
341   // ---------------------------------------------------------------------------
342 
343   const std::unique_ptr<Source> source(
344       Source::CreateFromModule(Context, pOutputFilepath, module, true));
345   RSScript script(*source);
346 
347   // Embed the info string directly in the ELF
348   script.setEmbedInfo(true);
349   script.setOptimizationLevel(RSScript::kOptLvl3);
350   script.setEmbedGlobalInfo(mEmbedGlobalInfo);
351   script.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant);
352 
353   llvm::SmallString<80> output_path(pOutputFilepath);
354   llvm::sys::path::replace_extension(output_path, ".o");
355 
356   // Pick the right runtime lib
357   const char* coreLibPath = pRuntimePath;
358   if (strcmp(pRuntimeRelaxedPath, "")) {
359       bcinfo::MetadataExtractor me(&module);
360       me.extract();
361       if (me.getRSFloatPrecision() == bcinfo::RS_FP_Relaxed) {
362           coreLibPath = pRuntimeRelaxedPath;
363       }
364   }
365 
366   compileScript(script, pOutputFilepath, output_path.c_str(), coreLibPath,
367                 buildChecksum, dumpIR);
368 
369   return true;
370 }
371 
buildForCompatLib(RSScript & pScript,const char * pOut,const char * pBuildChecksum,const char * pRuntimePath,bool pDumpIR)372 bool RSCompilerDriver::buildForCompatLib(RSScript &pScript, const char *pOut,
373                                          const char *pBuildChecksum,
374                                          const char *pRuntimePath,
375                                          bool pDumpIR) {
376   // Embed the info string directly in the ELF, since this path is for an
377   // offline (host) compilation.
378   pScript.setEmbedInfo(true);
379 
380   pScript.setEmbedGlobalInfo(mEmbedGlobalInfo);
381   pScript.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant);
382   pScript.setLinkRuntimeCallback(getLinkRuntimeCallback());
383 
384   Compiler::ErrorCode status = compileScript(pScript, pOut, pOut, pRuntimePath,
385                                              pBuildChecksum, pDumpIR);
386   if (status != Compiler::kSuccess) {
387     return false;
388   }
389 
390   return true;
391 }
392