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