• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/Support/Path.h>
20 
21 #include "bcinfo/BitcodeWrapper.h"
22 
23 #include "bcc/Renderscript/RSExecutable.h"
24 #include "bcc/Renderscript/RSScript.h"
25 #include "bcc/Support/CompilerConfig.h"
26 #include "bcc/Support/TargetCompilerConfigs.h"
27 #include "bcc/Source.h"
28 #include "bcc/Support/FileMutex.h"
29 #include "bcc/Support/Log.h"
30 #include "bcc/Support/InputFile.h"
31 #include "bcc/Support/Initialization.h"
32 #include "bcc/Support/Sha1Util.h"
33 #include "bcc/Support/OutputFile.h"
34 
35 #include <cutils/properties.h>
36 #include <utils/String8.h>
37 #include <utils/StopWatch.h>
38 
39 using namespace bcc;
40 
41 namespace {
42 
is_force_recompile()43 bool is_force_recompile() {
44   char buf[PROPERTY_VALUE_MAX];
45 
46   // Re-compile if floating point precision has been overridden.
47   property_get("debug.rs.precision", buf, "");
48   if (buf[0] != '\0') {
49     return true;
50   }
51 
52   // Re-compile if debug.rs.forcerecompile is set.
53   property_get("debug.rs.forcerecompile", buf, "0");
54   if ((::strcmp(buf, "1") == 0) || (::strcmp(buf, "true") == 0)) {
55     return true;
56   } else {
57     return false;
58   }
59 }
60 
61 } // end anonymous namespace
62 
RSCompilerDriver(bool pUseCompilerRT)63 RSCompilerDriver::RSCompilerDriver(bool pUseCompilerRT) :
64     mConfig(NULL), mCompiler(), mCompilerRuntime(NULL), mDebugContext(false) {
65   init::Initialize();
66   // Chain the symbol resolvers for compiler_rt and RS runtimes.
67   if (pUseCompilerRT) {
68     mCompilerRuntime = new CompilerRTSymbolResolver();
69     mResolver.chainResolver(*mCompilerRuntime);
70   }
71   mResolver.chainResolver(mRSRuntime);
72 }
73 
~RSCompilerDriver()74 RSCompilerDriver::~RSCompilerDriver() {
75   delete mCompilerRuntime;
76   delete mConfig;
77 }
78 
79 RSExecutable *
loadScriptCache(const char * pOutputPath,const RSInfo::DependencyTableTy & pDeps)80 RSCompilerDriver::loadScriptCache(const char *pOutputPath,
81                                   const RSInfo::DependencyTableTy &pDeps) {
82   //android::StopWatch load_time("bcc: RSCompilerDriver::loadScriptCache time");
83   RSExecutable *result = NULL;
84 
85   if (is_force_recompile())
86     return NULL;
87 
88   //===--------------------------------------------------------------------===//
89   // Acquire the read lock for reading output object file.
90   //===--------------------------------------------------------------------===//
91   FileMutex<FileBase::kReadLock> read_output_mutex(pOutputPath);
92 
93   if (read_output_mutex.hasError() || !read_output_mutex.lock()) {
94     ALOGE("Unable to acquire the read lock for %s! (%s)", pOutputPath,
95           read_output_mutex.getErrorMessage().c_str());
96     return NULL;
97   }
98 
99   //===--------------------------------------------------------------------===//
100   // Read the output object file.
101   //===--------------------------------------------------------------------===//
102   InputFile *output_file = new (std::nothrow) InputFile(pOutputPath);
103 
104   if ((output_file == NULL) || output_file->hasError()) {
105       //      ALOGE("Unable to open the %s for read! (%s)", pOutputPath,
106       //            output_file->getErrorMessage().c_str());
107     delete output_file;
108     return NULL;
109   }
110 
111   //===--------------------------------------------------------------------===//
112   // Acquire the read lock on output_file for reading its RS info file.
113   //===--------------------------------------------------------------------===//
114   android::String8 info_path = RSInfo::GetPath(*output_file);
115 
116   if (!output_file->lock()) {
117     ALOGE("Unable to acquire the read lock on %s for reading %s! (%s)",
118           pOutputPath, info_path.string(),
119           output_file->getErrorMessage().c_str());
120     delete output_file;
121     return NULL;
122   }
123 
124  //===---------------------------------------------------------------------===//
125   // Open and load the RS info file.
126   //===--------------------------------------------------------------------===//
127   InputFile info_file(info_path.string());
128   RSInfo *info = RSInfo::ReadFromFile(info_file, pDeps);
129 
130   // Release the lock on output_file.
131   output_file->unlock();
132 
133   if (info == NULL) {
134     delete output_file;
135     return NULL;
136   }
137 
138   //===--------------------------------------------------------------------===//
139   // Create the RSExecutable.
140   //===--------------------------------------------------------------------===//
141   result = RSExecutable::Create(*info, *output_file, mResolver);
142   if (result == NULL) {
143     delete output_file;
144     delete info;
145     return NULL;
146   }
147 
148   return result;
149 }
150 
setupConfig(const RSScript & pScript)151 bool RSCompilerDriver::setupConfig(const RSScript &pScript) {
152   bool changed = false;
153 
154   const llvm::CodeGenOpt::Level script_opt_level =
155       static_cast<llvm::CodeGenOpt::Level>(pScript.getOptimizationLevel());
156 
157   if (mConfig != NULL) {
158     // Renderscript bitcode may have their optimization flag configuration
159     // different than the previous run of RS compilation.
160     if (mConfig->getOptimizationLevel() != script_opt_level) {
161       mConfig->setOptimizationLevel(script_opt_level);
162       changed = true;
163     }
164   } else {
165     // Haven't run the compiler ever.
166     mConfig = new (std::nothrow) DefaultCompilerConfig();
167     if (mConfig == NULL) {
168       // Return false since mConfig remains NULL and out-of-memory.
169       return false;
170     }
171     mConfig->setOptimizationLevel(script_opt_level);
172     changed = true;
173   }
174 
175 #if defined(DEFAULT_ARM_CODEGEN)
176   // NEON should be disable when full-precision floating point is required.
177   assert((pScript.getInfo() != NULL) && "NULL RS info!");
178   if (pScript.getInfo()->getFloatPrecisionRequirement() == RSInfo::FP_Full) {
179     // Must be ARMCompilerConfig.
180     ARMCompilerConfig *arm_config = static_cast<ARMCompilerConfig *>(mConfig);
181     changed |= arm_config->enableNEON(/* pEnable */false);
182   }
183 #endif
184 
185   return changed;
186 }
187 
188 RSExecutable *
compileScript(RSScript & pScript,const char * pScriptName,const char * pOutputPath,const char * pRuntimePath,const RSInfo::DependencyTableTy & pDeps,bool pSkipLoad)189 RSCompilerDriver::compileScript(RSScript &pScript,
190                                 const char* pScriptName,
191                                 const char *pOutputPath,
192                                 const char *pRuntimePath,
193                                 const RSInfo::DependencyTableTy &pDeps,
194                                 bool pSkipLoad) {
195   //android::StopWatch compile_time("bcc: RSCompilerDriver::compileScript time");
196   RSExecutable *result = NULL;
197   RSInfo *info = NULL;
198 
199   //===--------------------------------------------------------------------===//
200   // Extract RS-specific information from source bitcode.
201   //===--------------------------------------------------------------------===//
202   // RS info may contains configuration (such as #optimization_level) to the
203   // compiler therefore it should be extracted before compilation.
204   info = RSInfo::ExtractFromSource(pScript.getSource(), pDeps);
205   if (info == NULL) {
206     return NULL;
207   }
208 
209   //===--------------------------------------------------------------------===//
210   // Associate script with its info
211   //===--------------------------------------------------------------------===//
212   // This is required since RS compiler may need information in the info file
213   // to do some transformation (e.g., expand foreach-able function.)
214   pScript.setInfo(info);
215 
216   //===--------------------------------------------------------------------===//
217   // Link RS script with Renderscript runtime.
218   //===--------------------------------------------------------------------===//
219   if (!RSScript::LinkRuntime(pScript, pRuntimePath)) {
220     ALOGE("Failed to link script '%s' with Renderscript runtime!", pScriptName);
221     return NULL;
222   }
223 
224   //===--------------------------------------------------------------------===//
225   // Acquire the write lock for writing output object file.
226   //===--------------------------------------------------------------------===//
227   FileMutex<FileBase::kWriteLock> write_output_mutex(pOutputPath);
228 
229   if (write_output_mutex.hasError() || !write_output_mutex.lock()) {
230     ALOGE("Unable to acquire the lock for writing %s! (%s)",
231           pOutputPath, write_output_mutex.getErrorMessage().c_str());
232     return NULL;
233   }
234 
235   //===--------------------------------------------------------------------===//
236   // Open the output file for write.
237   //===--------------------------------------------------------------------===//
238   unsigned flags = FileBase::kTruncate;
239   if (mDebugContext) {
240     // Delete the cache file when we finish up under a debug context.
241     flags |= FileBase::kDeleteOnClose;
242   }
243   OutputFile *output_file = new (std::nothrow) OutputFile(pOutputPath, flags);
244 
245   if ((output_file == NULL) || output_file->hasError()) {
246       ALOGE("Unable to open %s for write! (%s)", pOutputPath,
247             output_file->getErrorMessage().c_str());
248     delete info;
249     delete output_file;
250     return NULL;
251   }
252 
253   //===--------------------------------------------------------------------===//
254   // Setup the config to the compiler.
255   //===--------------------------------------------------------------------===//
256   bool compiler_need_reconfigure = setupConfig(pScript);
257 
258   if (mConfig == NULL) {
259     ALOGE("Failed to setup config for RS compiler to compile %s!", pOutputPath);
260     delete info;
261     delete output_file;
262     return NULL;
263   }
264 
265   // Compiler need to re-config if it's haven't run the config() yet or the
266   // configuration it referenced is changed.
267   if (compiler_need_reconfigure) {
268     Compiler::ErrorCode err = mCompiler.config(*mConfig);
269     if (err != Compiler::kSuccess) {
270       ALOGE("Failed to config the RS compiler for %s! (%s)",pOutputPath,
271             Compiler::GetErrorString(err));
272       delete info;
273       delete output_file;
274       return NULL;
275     }
276   }
277 
278   //===--------------------------------------------------------------------===//
279   // Run the compiler.
280   //===--------------------------------------------------------------------===//
281   Compiler::ErrorCode compile_result = mCompiler.compile(pScript, *output_file);
282   if (compile_result != Compiler::kSuccess) {
283     ALOGE("Unable to compile the source to file %s! (%s)", pOutputPath,
284           Compiler::GetErrorString(compile_result));
285     delete info;
286     delete output_file;
287     return NULL;
288   }
289 
290   // No need to produce an RSExecutable in this case.
291   // TODO: Error handling in this case is nonexistent.
292   if (pSkipLoad) {
293     return NULL;
294   }
295 
296   //===--------------------------------------------------------------------===//
297   // Create the RSExecutable.
298   //===--------------------------------------------------------------------===//
299   result = RSExecutable::Create(*info, *output_file, mResolver);
300   if (result == NULL) {
301     delete info;
302     delete output_file;
303     return NULL;
304   }
305 
306   //===--------------------------------------------------------------------===//
307   // Dump the disassembly for debug when possible.
308   //===--------------------------------------------------------------------===//
309 #if USE_DISASSEMBLER
310   OutputFile *disassembly_output =
311       new (std::nothrow) OutputFile(DEBUG_DISASSEMBLER_FILE,
312                                     FileBase::kAppend);
313 
314   if (disassembly_output != NULL) {
315     result->dumpDisassembly(*disassembly_output);
316     delete disassembly_output;
317   }
318 #endif
319 
320   //===--------------------------------------------------------------------===//
321   // Write out the RS info file.
322   //===--------------------------------------------------------------------===//
323   // Note that write failure only results in a warning since the source is
324   // successfully compiled and loaded.
325   if (!result->syncInfo(/* pForce */true)) {
326     ALOGW("%s was successfully compiled and loaded but its RS info file failed "
327           "to write out!", pOutputPath);
328   }
329 
330   return result;
331 }
332 
build(BCCContext & pContext,const char * pCacheDir,const char * pResName,const char * pBitcode,size_t pBitcodeSize,const char * pRuntimePath,RSLinkRuntimeCallback pLinkRuntimeCallback)333 RSExecutable *RSCompilerDriver::build(BCCContext &pContext,
334                                       const char *pCacheDir,
335                                       const char *pResName,
336                                       const char *pBitcode,
337                                       size_t pBitcodeSize,
338                                       const char *pRuntimePath,
339                                       RSLinkRuntimeCallback pLinkRuntimeCallback) {
340     //  android::StopWatch build_time("bcc: RSCompilerDriver::build time");
341   //===--------------------------------------------------------------------===//
342   // Check parameters.
343   //===--------------------------------------------------------------------===//
344   if ((pCacheDir == NULL) || (pResName == NULL)) {
345     ALOGE("Invalid parameter passed to RSCompilerDriver::build()! (cache dir: "
346           "%s, resource name: %s)", ((pCacheDir) ? pCacheDir : "(null)"),
347                                     ((pResName) ? pResName : "(null)"));
348     return NULL;
349   }
350 
351   if ((pBitcode == NULL) || (pBitcodeSize <= 0)) {
352     ALOGE("No bitcode supplied! (bitcode: %p, size of bitcode: %u)",
353           pBitcode, static_cast<unsigned>(pBitcodeSize));
354     return NULL;
355   }
356 
357   //===--------------------------------------------------------------------===//
358   // Prepare dependency information.
359   //===--------------------------------------------------------------------===//
360   RSInfo::DependencyTableTy dep_info;
361   uint8_t bitcode_sha1[20];
362   Sha1Util::GetSHA1DigestFromBuffer(bitcode_sha1, pBitcode, pBitcodeSize);
363   dep_info.push(std::make_pair(pResName, bitcode_sha1));
364 
365   //===--------------------------------------------------------------------===//
366   // Construct output path.
367   //===--------------------------------------------------------------------===//
368   llvm::sys::Path output_path(pCacheDir);
369 
370   // {pCacheDir}/{pResName}
371   if (!output_path.appendComponent(pResName)) {
372     ALOGE("Failed to construct output path %s/%s!", pCacheDir, pResName);
373     return NULL;
374   }
375 
376   // {pCacheDir}/{pResName}.o
377   output_path.appendSuffix("o");
378 
379   //===--------------------------------------------------------------------===//
380   // Load cache.
381   //===--------------------------------------------------------------------===//
382   RSExecutable *result = NULL;
383 
384   // Skip loading from the cache if we are using a debug context.
385   if (!mDebugContext) {
386     result = loadScriptCache(output_path.c_str(), dep_info);
387 
388     if (result != NULL) {
389       // Cache hit
390       return result;
391     }
392   }
393 
394   //===--------------------------------------------------------------------===//
395   // Load the bitcode and create script.
396   //===--------------------------------------------------------------------===//
397   Source *source = Source::CreateFromBuffer(pContext, pResName,
398                                             pBitcode, pBitcodeSize);
399   if (source == NULL) {
400     return NULL;
401   }
402 
403   RSScript *script = new (std::nothrow) RSScript(*source);
404   if (script == NULL) {
405     ALOGE("Out of memory when create Script object for '%s'! (output: %s)",
406           pResName, output_path.c_str());
407     delete source;
408     return NULL;
409   }
410 
411   script->setLinkRuntimeCallback(pLinkRuntimeCallback);
412 
413   // Read information from bitcode wrapper.
414   bcinfo::BitcodeWrapper wrapper(pBitcode, pBitcodeSize);
415   script->setCompilerVersion(wrapper.getCompilerVersion());
416   script->setOptimizationLevel(static_cast<RSScript::OptimizationLevel>(
417                                    wrapper.getOptimizationLevel()));
418 
419   //===--------------------------------------------------------------------===//
420   // Compile the script
421   //===--------------------------------------------------------------------===//
422   result = compileScript(*script, pResName, output_path.c_str(), pRuntimePath,
423                          dep_info, false);
424 
425   // Script is no longer used. Free it to get more memory.
426   delete script;
427 
428   if (result == NULL) {
429     return NULL;
430   }
431 
432   return result;
433 }
434 
435 
build(RSScript & pScript,const char * pOut,const char * pRuntimePath)436 RSExecutable *RSCompilerDriver::build(RSScript &pScript, const char *pOut,
437                                       const char *pRuntimePath) {
438   RSInfo::DependencyTableTy dep_info;
439   RSInfo *info = RSInfo::ExtractFromSource(pScript.getSource(), dep_info);
440   if (info == NULL) {
441     return NULL;
442   }
443   pScript.setInfo(info);
444 
445   // Embed the info string directly in the ELF, since this path is for an
446   // offline (host) compilation.
447   pScript.setEmbedInfo(true);
448 
449   RSExecutable *result = compileScript(pScript, pOut, pOut, pRuntimePath,
450                                        dep_info, true);
451   return result;
452 }
453 
454