RSCompilerDriver.cpp revision 107f50d54d9606e495187d0e89225d3d9fdc1fa9
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 <string>
18
19#include "bcc/Renderscript/RSCompilerDriver.h"
20
21#include <llvm/IR/Module.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
28#include "bcc/Compiler.h"
29#include "bcc/Config/Config.h"
30#include "bcc/Renderscript/RSInfo.h"
31#include "bcc/Renderscript/RSScript.h"
32#include "bcc/Support/CompilerConfig.h"
33#include "bcc/Source.h"
34#include "bcc/Support/FileMutex.h"
35#include "bcc/Support/Log.h"
36#include "bcc/Support/InputFile.h"
37#include "bcc/Support/Initialization.h"
38#include "bcc/Support/Sha1Util.h"
39#include "bcc/Support/OutputFile.h"
40
41#ifdef HAVE_ANDROID_OS
42#include <cutils/properties.h>
43#endif
44#include <utils/StopWatch.h>
45
46using namespace bcc;
47
48// Get the build fingerprint of the Android device we are running on.
49static std::string getBuildFingerPrint() {
50#ifdef HAVE_ANDROID_OS
51    char fingerprint[PROPERTY_VALUE_MAX];
52    property_get("ro.build.fingerprint", fingerprint, "");
53    return fingerprint;
54#else
55    return "HostBuild";
56#endif
57}
58
59RSCompilerDriver::RSCompilerDriver(bool pUseCompilerRT) :
60    mConfig(nullptr), mCompiler(), mDebugContext(false),
61    mLinkRuntimeCallback(nullptr), mEnableGlobalMerge(true) {
62  init::Initialize();
63}
64
65RSCompilerDriver::~RSCompilerDriver() {
66  delete mConfig;
67}
68
69
70#if defined(PROVIDE_ARM_CODEGEN)
71extern llvm::cl::opt<bool> EnableGlobalMerge;
72#endif
73
74bool RSCompilerDriver::setupConfig(const RSScript &pScript) {
75  bool changed = false;
76
77  const llvm::CodeGenOpt::Level script_opt_level =
78      static_cast<llvm::CodeGenOpt::Level>(pScript.getOptimizationLevel());
79
80#if defined(PROVIDE_ARM_CODEGEN)
81  EnableGlobalMerge = mEnableGlobalMerge;
82#endif
83
84  if (mConfig != nullptr) {
85    // Renderscript bitcode may have their optimization flag configuration
86    // different than the previous run of RS compilation.
87    if (mConfig->getOptimizationLevel() != script_opt_level) {
88      mConfig->setOptimizationLevel(script_opt_level);
89      changed = true;
90    }
91  } else {
92    // Haven't run the compiler ever.
93    mConfig = new (std::nothrow) CompilerConfig(DEFAULT_TARGET_TRIPLE_STRING);
94    if (mConfig == nullptr) {
95      // Return false since mConfig remains NULL and out-of-memory.
96      return false;
97    }
98    mConfig->setOptimizationLevel(script_opt_level);
99    changed = true;
100  }
101
102#if defined(PROVIDE_ARM_CODEGEN)
103  assert((pScript.getInfo() != nullptr) && "NULL RS info!");
104  bool script_full_prec = (pScript.getInfo()->getFloatPrecisionRequirement() ==
105                           RSInfo::FP_Full);
106  if (mConfig->getFullPrecision() != script_full_prec) {
107    mConfig->setFullPrecision(script_full_prec);
108    changed = true;
109  }
110#endif
111
112  return changed;
113}
114
115Compiler::ErrorCode RSCompilerDriver::compileScript(RSScript& pScript, const char* pScriptName,
116                                                    const char* pOutputPath,
117                                                    const char* pRuntimePath,
118                                                    const RSInfo::DependencyHashTy& pSourceHash,
119                                                    const char* compileCommandLineToEmbed,
120                                                    bool saveInfoFile, bool pDumpIR) {
121  // android::StopWatch compile_time("bcc: RSCompilerDriver::compileScript time");
122  RSInfo *info = nullptr;
123
124  //===--------------------------------------------------------------------===//
125  // Extract RS-specific information from source bitcode.
126  //===--------------------------------------------------------------------===//
127  // RS info may contains configuration (such as #optimization_level) to the
128  // compiler therefore it should be extracted before compilation.
129  info = RSInfo::ExtractFromSource(pScript.getSource(), pSourceHash, compileCommandLineToEmbed,
130                                   getBuildFingerPrint().c_str());
131  if (info == nullptr) {
132    return Compiler::kErrInvalidSource;
133  }
134
135  //===--------------------------------------------------------------------===//
136  // Associate script with its info
137  //===--------------------------------------------------------------------===//
138  // This is required since RS compiler may need information in the info file
139  // to do some transformation (e.g., expand foreach-able function.)
140  pScript.setInfo(info);
141
142  //===--------------------------------------------------------------------===//
143  // Link RS script with Renderscript runtime.
144  //===--------------------------------------------------------------------===//
145  if (!RSScript::LinkRuntime(pScript, pRuntimePath)) {
146    ALOGE("Failed to link script '%s' with Renderscript runtime!", pScriptName);
147    return Compiler::kErrInvalidSource;
148  }
149
150  {
151    // FIXME(srhines): Windows compilation can't use locking like this, but
152    // we also don't need to worry about concurrent writers of the same file.
153#ifndef USE_MINGW
154    //===------------------------------------------------------------------===//
155    // Acquire the write lock for writing output object file.
156    //===------------------------------------------------------------------===//
157    FileMutex<FileBase::kWriteLock> write_output_mutex(pOutputPath);
158
159    if (write_output_mutex.hasError() || !write_output_mutex.lock()) {
160      ALOGE("Unable to acquire the lock for writing %s! (%s)",
161            pOutputPath, write_output_mutex.getErrorMessage().c_str());
162      return Compiler::kErrInvalidSource;
163    }
164#endif
165
166    // Open the output file for write.
167    OutputFile output_file(pOutputPath,
168                           FileBase::kTruncate | FileBase::kBinary);
169
170    if (output_file.hasError()) {
171        ALOGE("Unable to open %s for write! (%s)", pOutputPath,
172              output_file.getErrorMessage().c_str());
173      return Compiler::kErrInvalidSource;
174    }
175
176    // Setup the config to the compiler.
177    bool compiler_need_reconfigure = setupConfig(pScript);
178
179    if (mConfig == nullptr) {
180      ALOGE("Failed to setup config for RS compiler to compile %s!",
181            pOutputPath);
182      return Compiler::kErrInvalidSource;
183    }
184
185    if (compiler_need_reconfigure) {
186      Compiler::ErrorCode err = mCompiler.config(*mConfig);
187      if (err != Compiler::kSuccess) {
188        ALOGE("Failed to config the RS compiler for %s! (%s)",pOutputPath,
189              Compiler::GetErrorString(err));
190        return Compiler::kErrInvalidSource;
191      }
192    }
193
194    OutputFile *ir_file = nullptr;
195    llvm::raw_fd_ostream *IRStream = nullptr;
196    if (pDumpIR) {
197      std::string path(pOutputPath);
198      path.append(".ll");
199      ir_file = new OutputFile(path.c_str(), FileBase::kTruncate);
200      IRStream = ir_file->dup();
201    }
202
203    // Run the compiler.
204    Compiler::ErrorCode compile_result =
205        mCompiler.compile(pScript, output_file, IRStream);
206
207    if (ir_file) {
208      ir_file->close();
209      delete ir_file;
210    }
211
212    if (compile_result != Compiler::kSuccess) {
213      ALOGE("Unable to compile the source to file %s! (%s)", pOutputPath,
214            Compiler::GetErrorString(compile_result));
215      return Compiler::kErrInvalidSource;
216    }
217  }
218
219  if (saveInfoFile) {
220    std::string info_path = RSInfo::GetPath(pOutputPath);
221    OutputFile info_file(info_path.c_str(), FileBase::kTruncate);
222
223    if (info_file.hasError()) {
224      ALOGE("Failed to open the info file %s for write! (%s)",
225            info_path.c_str(), info_file.getErrorMessage().c_str());
226      return Compiler::kErrInvalidSource;
227    }
228
229    FileMutex<FileBase::kWriteLock> write_info_mutex(info_path.c_str());
230    if (write_info_mutex.hasError() || !write_info_mutex.lock()) {
231      ALOGE("Unable to acquire the lock for writing %s! (%s)",
232            info_path.c_str(), write_info_mutex.getErrorMessage().c_str());
233      return Compiler::kErrInvalidSource;
234    }
235
236    // Perform the write.
237    if (!info->write(info_file)) {
238      ALOGE("Failed to sync the RS info file %s!", info_path.c_str());
239      return Compiler::kErrInvalidSource;
240    }
241  }
242
243  return Compiler::kSuccess;
244}
245
246bool RSCompilerDriver::build(BCCContext &pContext,
247                             const char *pCacheDir,
248                             const char *pResName,
249                             const char *pBitcode,
250                             size_t pBitcodeSize,
251                             const char *commandLine,
252                             const char *pRuntimePath,
253                             RSLinkRuntimeCallback pLinkRuntimeCallback,
254                             bool pDumpIR) {
255    //  android::StopWatch build_time("bcc: RSCompilerDriver::build time");
256  //===--------------------------------------------------------------------===//
257  // Check parameters.
258  //===--------------------------------------------------------------------===//
259  if ((pCacheDir == nullptr) || (pResName == nullptr)) {
260    ALOGE("Invalid parameter passed to RSCompilerDriver::build()! (cache dir: "
261          "%s, resource name: %s)", ((pCacheDir) ? pCacheDir : "(null)"),
262                                    ((pResName) ? pResName : "(null)"));
263    return false;
264  }
265
266  if ((pBitcode == nullptr) || (pBitcodeSize <= 0)) {
267    ALOGE("No bitcode supplied! (bitcode: %p, size of bitcode: %u)",
268          pBitcode, static_cast<unsigned>(pBitcodeSize));
269    return false;
270  }
271
272  //===--------------------------------------------------------------------===//
273  // Prepare dependency information.
274  //===--------------------------------------------------------------------===//
275  uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH];
276  Sha1Util::GetSHA1DigestFromBuffer(bitcode_sha1, pBitcode, pBitcodeSize);
277
278  //===--------------------------------------------------------------------===//
279  // Construct output path.
280  // {pCacheDir}/{pResName}.o
281  //===--------------------------------------------------------------------===//
282  llvm::SmallString<80> output_path(pCacheDir);
283  llvm::sys::path::append(output_path, pResName);
284  llvm::sys::path::replace_extension(output_path, ".o");
285
286  //===--------------------------------------------------------------------===//
287  // Load the bitcode and create script.
288  //===--------------------------------------------------------------------===//
289  Source *source = Source::CreateFromBuffer(pContext, pResName,
290                                            pBitcode, pBitcodeSize);
291  if (source == nullptr) {
292    return false;
293  }
294
295  RSScript script(*source);
296  if (pLinkRuntimeCallback) {
297    setLinkRuntimeCallback(pLinkRuntimeCallback);
298  }
299
300  script.setLinkRuntimeCallback(getLinkRuntimeCallback());
301
302  // Read information from bitcode wrapper.
303  bcinfo::BitcodeWrapper wrapper(pBitcode, pBitcodeSize);
304  script.setCompilerVersion(wrapper.getCompilerVersion());
305  script.setOptimizationLevel(static_cast<RSScript::OptimizationLevel>(
306                              wrapper.getOptimizationLevel()));
307
308  //===--------------------------------------------------------------------===//
309  // Compile the script
310  //===--------------------------------------------------------------------===//
311  Compiler::ErrorCode status = compileScript(script, pResName,
312                                             output_path.c_str(),
313                                             pRuntimePath, bitcode_sha1, commandLine,
314                                             true, pDumpIR);
315
316  return status == Compiler::kSuccess;
317}
318
319
320bool RSCompilerDriver::buildForCompatLib(RSScript &pScript, const char *pOut,
321                                         const char *pRuntimePath) {
322  // For compat lib, we don't check the RS info file so we don't need the source hash,
323  // compile command, and build fingerprint.
324  // TODO We may want to make them optional or embed real values.
325  uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH] = {0};
326  const char* compileCommandLineToEmbed = "";
327  const char* buildFingerprintToEmbed = "";
328
329  RSInfo* info = RSInfo::ExtractFromSource(pScript.getSource(), bitcode_sha1,
330                                           compileCommandLineToEmbed, buildFingerprintToEmbed);
331  if (info == nullptr) {
332    return false;
333  }
334  pScript.setInfo(info);
335
336  // Embed the info string directly in the ELF, since this path is for an
337  // offline (host) compilation.
338  pScript.setEmbedInfo(true);
339
340  Compiler::ErrorCode status = compileScript(pScript, pOut, pOut, pRuntimePath, bitcode_sha1,
341                                             compileCommandLineToEmbed, false, false);
342  if (status != Compiler::kSuccess) {
343    return false;
344  }
345
346  return true;
347}
348