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