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