RSCompilerDriver.cpp revision 6da4e253a513feef3405759fef6d0760828808ca
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 %s!",
159          pScriptName, pRuntimePath);
160    return Compiler::kErrInvalidSource;
161  }
162
163  {
164    // FIXME(srhines): Windows compilation can't use locking like this, but
165    // we also don't need to worry about concurrent writers of the same file.
166#ifndef USE_MINGW
167    //===------------------------------------------------------------------===//
168    // Acquire the write lock for writing output object file.
169    //===------------------------------------------------------------------===//
170    FileMutex<FileBase::kWriteLock> write_output_mutex(pOutputPath);
171
172    if (write_output_mutex.hasError() || !write_output_mutex.lock()) {
173      ALOGE("Unable to acquire the lock for writing %s! (%s)",
174            pOutputPath, write_output_mutex.getErrorMessage().c_str());
175      return Compiler::kErrInvalidSource;
176    }
177#endif
178
179    // Open the output file for write.
180    OutputFile output_file(pOutputPath,
181                           FileBase::kTruncate | FileBase::kBinary);
182
183    if (output_file.hasError()) {
184        ALOGE("Unable to open %s for write! (%s)", pOutputPath,
185              output_file.getErrorMessage().c_str());
186      return Compiler::kErrInvalidSource;
187    }
188
189    // Setup the config to the compiler.
190    bool compiler_need_reconfigure = setupConfig(pScript);
191
192    if (mConfig == nullptr) {
193      ALOGE("Failed to setup config for RS compiler to compile %s!",
194            pOutputPath);
195      return Compiler::kErrInvalidSource;
196    }
197
198    if (compiler_need_reconfigure) {
199      Compiler::ErrorCode err = mCompiler.config(*mConfig);
200      if (err != Compiler::kSuccess) {
201        ALOGE("Failed to config the RS compiler for %s! (%s)",pOutputPath,
202              Compiler::GetErrorString(err));
203        return Compiler::kErrInvalidSource;
204      }
205    }
206
207    OutputFile *ir_file = nullptr;
208    llvm::raw_fd_ostream *IRStream = nullptr;
209    if (pDumpIR) {
210      std::string path(pOutputPath);
211      path.append(".ll");
212      ir_file = new OutputFile(path.c_str(), FileBase::kTruncate);
213      IRStream = ir_file->dup();
214    }
215
216    // Run the compiler.
217    Compiler::ErrorCode compile_result =
218        mCompiler.compile(pScript, output_file, IRStream);
219
220    if (ir_file) {
221      ir_file->close();
222      delete ir_file;
223    }
224
225    if (compile_result != Compiler::kSuccess) {
226      ALOGE("Unable to compile the source to file %s! (%s)", pOutputPath,
227            Compiler::GetErrorString(compile_result));
228      return Compiler::kErrInvalidSource;
229    }
230  }
231
232  if (saveInfoFile) {
233    std::string info_path = RSInfo::GetPath(pOutputPath);
234    OutputFile info_file(info_path.c_str(), FileBase::kTruncate);
235
236    if (info_file.hasError()) {
237      ALOGE("Failed to open the info file %s for write! (%s)",
238            info_path.c_str(), info_file.getErrorMessage().c_str());
239      return Compiler::kErrInvalidSource;
240    }
241
242    FileMutex<FileBase::kWriteLock> write_info_mutex(info_path.c_str());
243    if (write_info_mutex.hasError() || !write_info_mutex.lock()) {
244      ALOGE("Unable to acquire the lock for writing %s! (%s)",
245            info_path.c_str(), write_info_mutex.getErrorMessage().c_str());
246      return Compiler::kErrInvalidSource;
247    }
248
249    // Perform the write.
250    if (!info->write(info_file)) {
251      ALOGE("Failed to sync the RS info file %s!", info_path.c_str());
252      return Compiler::kErrInvalidSource;
253    }
254  }
255
256  return Compiler::kSuccess;
257}
258
259bool RSCompilerDriver::build(BCCContext &pContext,
260                             const char *pCacheDir,
261                             const char *pResName,
262                             const char *pBitcode,
263                             size_t pBitcodeSize,
264                             const char *commandLine,
265                             const char *pBuildChecksum,
266                             const char *pRuntimePath,
267                             RSLinkRuntimeCallback pLinkRuntimeCallback,
268                             bool pDumpIR) {
269    //  android::StopWatch build_time("bcc: RSCompilerDriver::build time");
270  //===--------------------------------------------------------------------===//
271  // Check parameters.
272  //===--------------------------------------------------------------------===//
273  if ((pCacheDir == nullptr) || (pResName == nullptr)) {
274    ALOGE("Invalid parameter passed to RSCompilerDriver::build()! (cache dir: "
275          "%s, resource name: %s)", ((pCacheDir) ? pCacheDir : "(null)"),
276                                    ((pResName) ? pResName : "(null)"));
277    return false;
278  }
279
280  if ((pBitcode == nullptr) || (pBitcodeSize <= 0)) {
281    ALOGE("No bitcode supplied! (bitcode: %p, size of bitcode: %u)",
282          pBitcode, static_cast<unsigned>(pBitcodeSize));
283    return false;
284  }
285
286  //===--------------------------------------------------------------------===//
287  // Prepare dependency information.
288  //===--------------------------------------------------------------------===//
289  uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH];
290  Sha1Util::GetSHA1DigestFromBuffer(bitcode_sha1, pBitcode, pBitcodeSize);
291
292  //===--------------------------------------------------------------------===//
293  // Construct output path.
294  // {pCacheDir}/{pResName}.o
295  //===--------------------------------------------------------------------===//
296  llvm::SmallString<80> output_path(pCacheDir);
297  llvm::sys::path::append(output_path, pResName);
298  llvm::sys::path::replace_extension(output_path, ".o");
299
300  //===--------------------------------------------------------------------===//
301  // Load the bitcode and create script.
302  //===--------------------------------------------------------------------===//
303  Source *source = Source::CreateFromBuffer(pContext, pResName,
304                                            pBitcode, pBitcodeSize);
305  if (source == nullptr) {
306    return false;
307  }
308
309  RSScript script(*source);
310  if (pLinkRuntimeCallback) {
311    setLinkRuntimeCallback(pLinkRuntimeCallback);
312  }
313
314  script.setLinkRuntimeCallback(getLinkRuntimeCallback());
315
316  // Read information from bitcode wrapper.
317  bcinfo::BitcodeWrapper wrapper(pBitcode, pBitcodeSize);
318  script.setCompilerVersion(wrapper.getCompilerVersion());
319  script.setOptimizationLevel(static_cast<RSScript::OptimizationLevel>(
320                              wrapper.getOptimizationLevel()));
321
322  //===--------------------------------------------------------------------===//
323  // Compile the script
324  //===--------------------------------------------------------------------===//
325  Compiler::ErrorCode status = compileScript(script, pResName,
326                                             output_path.c_str(),
327                                             pRuntimePath, bitcode_sha1, commandLine,
328                                             pBuildChecksum, true, pDumpIR);
329
330  return status == Compiler::kSuccess;
331}
332
333bool RSCompilerDriver::buildScriptGroup(
334    BCCContext& Context, const char* pOutputFilepath, const char* pRuntimePath,
335    const char* pRuntimeRelaxedPath, bool dumpIR,
336    const std::vector<Source*>& sources,
337    const std::list<std::list<std::pair<int, int>>>& toFuse,
338    const std::list<std::string>& fused,
339    const std::list<std::list<std::pair<int, int>>>& invokes,
340    const std::list<std::string>& invokeBatchNames) {
341  // ---------------------------------------------------------------------------
342  // Link all input modules into a single module
343  // ---------------------------------------------------------------------------
344
345  llvm::LLVMContext& context = Context.getLLVMContext();
346  llvm::Module module("Merged Script Group", context);
347
348  llvm::Linker linker(&module);
349  for (Source* source : sources) {
350    if (linker.linkInModule(&source->getModule())) {
351      ALOGE("Linking for module in source failed.");
352      return false;
353    }
354  }
355
356  // ---------------------------------------------------------------------------
357  // Create fused kernels
358  // ---------------------------------------------------------------------------
359
360  auto inputIter = toFuse.begin();
361  for (const std::string& nameOfFused : fused) {
362    auto inputKernels = *inputIter++;
363    std::vector<Source*> sourcesToFuse;
364    std::vector<int> slots;
365
366    for (auto p : inputKernels) {
367      sourcesToFuse.push_back(sources[p.first]);
368      slots.push_back(p.second);
369    }
370
371    if (!fuseKernels(Context, sourcesToFuse, slots, nameOfFused, &module)) {
372      return false;
373    }
374  }
375
376  // ---------------------------------------------------------------------------
377  // Rename invokes
378  // ---------------------------------------------------------------------------
379
380  auto invokeIter = invokes.begin();
381  for (const std::string& newName : invokeBatchNames) {
382    auto inputInvoke = *invokeIter++;
383    auto p = inputInvoke.front();
384    Source* source = sources[p.first];
385    int slot = p.second;
386
387    if (!renameInvoke(Context, source, slot, newName, &module)) {
388      return false;
389    }
390  }
391
392  // ---------------------------------------------------------------------------
393  // Compile the new module with fused kernels
394  // ---------------------------------------------------------------------------
395
396  const std::unique_ptr<Source> source(
397      Source::CreateFromModule(Context, pOutputFilepath, module, true));
398  RSScript script(*source);
399
400  uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH];
401  const char* compileCommandLineToEmbed = "";
402  const char* buildChecksum = "DummyChecksumForScriptGroup";
403  const char* buildFingerprintToEmbed = "";
404
405  RSInfo* info = RSInfo::ExtractFromSource(*source, bitcode_sha1,
406                                           compileCommandLineToEmbed, buildFingerprintToEmbed);
407  if (info == nullptr) {
408    return false;
409  }
410  script.setInfo(info);
411
412  // Embed the info string directly in the ELF
413  script.setEmbedInfo(true);
414  script.setOptimizationLevel(RSScript::kOptLvl3);
415
416  llvm::SmallString<80> output_path(pOutputFilepath);
417  llvm::sys::path::replace_extension(output_path, ".o");
418
419  // Pick the right runtime lib
420  const char* coreLibPath = pRuntimePath;
421  if (strcmp(pRuntimeRelaxedPath, "")) {
422      bcinfo::MetadataExtractor me(&module);
423      me.extract();
424      if (me.getRSFloatPrecision() == bcinfo::RS_FP_Relaxed) {
425          coreLibPath = pRuntimeRelaxedPath;
426      }
427  }
428
429  compileScript(script, pOutputFilepath, output_path.c_str(), coreLibPath,
430                bitcode_sha1, compileCommandLineToEmbed, buildChecksum,
431                true, dumpIR);
432
433  return true;
434}
435
436bool RSCompilerDriver::buildForCompatLib(RSScript &pScript, const char *pOut,
437                                         const char *pBuildChecksum,
438                                         const char *pRuntimePath,
439                                         bool pDumpIR) {
440  // For compat lib, we don't check the RS info file so we don't need the source hash,
441  // compile command, and build fingerprint.
442  // TODO We may want to make them optional or embed real values.
443  uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH] = {0};
444  const char* compileCommandLineToEmbed = "";
445  const char* buildFingerprintToEmbed = "";
446
447  RSInfo* info = RSInfo::ExtractFromSource(pScript.getSource(), bitcode_sha1,
448                                           compileCommandLineToEmbed, buildFingerprintToEmbed);
449  if (info == nullptr) {
450    return false;
451  }
452  pScript.setInfo(info);
453
454  // Embed the info string directly in the ELF, since this path is for an
455  // offline (host) compilation.
456  pScript.setEmbedInfo(true);
457
458  Compiler::ErrorCode status = compileScript(pScript, pOut, pOut, pRuntimePath,
459                                             bitcode_sha1,
460                                             compileCommandLineToEmbed,
461                                             pBuildChecksum, false, pDumpIR);
462  if (status != Compiler::kSuccess) {
463    return false;
464  }
465
466  return true;
467}
468