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