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