RSCompilerDriver.cpp revision a4ded1373d7ad3e503f186e65bccf94126a0f020
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                                                    bool saveInfoFile, bool pDumpIR) {
123  // android::StopWatch compile_time("bcc: RSCompilerDriver::compileScript time");
124  RSInfo *info = nullptr;
125
126  //===--------------------------------------------------------------------===//
127  // Extract RS-specific information from source bitcode.
128  //===--------------------------------------------------------------------===//
129  // RS info may contains configuration (such as #optimization_level) to the
130  // compiler therefore it should be extracted before compilation.
131  info = RSInfo::ExtractFromSource(pScript.getSource(), pSourceHash, compileCommandLineToEmbed,
132                                   getBuildFingerPrint().c_str());
133  if (info == nullptr) {
134    return Compiler::kErrInvalidSource;
135  }
136
137  //===--------------------------------------------------------------------===//
138  // Associate script with its info
139  //===--------------------------------------------------------------------===//
140  // This is required since RS compiler may need information in the info file
141  // to do some transformation (e.g., expand foreach-able function.)
142  pScript.setInfo(info);
143
144  //===--------------------------------------------------------------------===//
145  // Link RS script with Renderscript runtime.
146  //===--------------------------------------------------------------------===//
147  if (!RSScript::LinkRuntime(pScript, pRuntimePath)) {
148    ALOGE("Failed to link script '%s' with Renderscript runtime!", pScriptName);
149    return Compiler::kErrInvalidSource;
150  }
151
152  {
153    // FIXME(srhines): Windows compilation can't use locking like this, but
154    // we also don't need to worry about concurrent writers of the same file.
155#ifndef USE_MINGW
156    //===------------------------------------------------------------------===//
157    // Acquire the write lock for writing output object file.
158    //===------------------------------------------------------------------===//
159    FileMutex<FileBase::kWriteLock> write_output_mutex(pOutputPath);
160
161    if (write_output_mutex.hasError() || !write_output_mutex.lock()) {
162      ALOGE("Unable to acquire the lock for writing %s! (%s)",
163            pOutputPath, write_output_mutex.getErrorMessage().c_str());
164      return Compiler::kErrInvalidSource;
165    }
166#endif
167
168    // Open the output file for write.
169    OutputFile output_file(pOutputPath,
170                           FileBase::kTruncate | FileBase::kBinary);
171
172    if (output_file.hasError()) {
173        ALOGE("Unable to open %s for write! (%s)", pOutputPath,
174              output_file.getErrorMessage().c_str());
175      return Compiler::kErrInvalidSource;
176    }
177
178    // Setup the config to the compiler.
179    bool compiler_need_reconfigure = setupConfig(pScript);
180
181    if (mConfig == nullptr) {
182      ALOGE("Failed to setup config for RS compiler to compile %s!",
183            pOutputPath);
184      return Compiler::kErrInvalidSource;
185    }
186
187    if (compiler_need_reconfigure) {
188      Compiler::ErrorCode err = mCompiler.config(*mConfig);
189      if (err != Compiler::kSuccess) {
190        ALOGE("Failed to config the RS compiler for %s! (%s)",pOutputPath,
191              Compiler::GetErrorString(err));
192        return Compiler::kErrInvalidSource;
193      }
194    }
195
196    OutputFile *ir_file = nullptr;
197    llvm::raw_fd_ostream *IRStream = nullptr;
198    if (pDumpIR) {
199      std::string path(pOutputPath);
200      path.append(".ll");
201      ir_file = new OutputFile(path.c_str(), FileBase::kTruncate);
202      IRStream = ir_file->dup();
203    }
204
205    // Run the compiler.
206    Compiler::ErrorCode compile_result =
207        mCompiler.compile(pScript, output_file, IRStream);
208
209    if (ir_file) {
210      ir_file->close();
211      delete ir_file;
212    }
213
214    if (compile_result != Compiler::kSuccess) {
215      ALOGE("Unable to compile the source to file %s! (%s)", pOutputPath,
216            Compiler::GetErrorString(compile_result));
217      return Compiler::kErrInvalidSource;
218    }
219  }
220
221  if (saveInfoFile) {
222    std::string info_path = RSInfo::GetPath(pOutputPath);
223    OutputFile info_file(info_path.c_str(), FileBase::kTruncate);
224
225    if (info_file.hasError()) {
226      ALOGE("Failed to open the info file %s for write! (%s)",
227            info_path.c_str(), info_file.getErrorMessage().c_str());
228      return Compiler::kErrInvalidSource;
229    }
230
231    FileMutex<FileBase::kWriteLock> write_info_mutex(info_path.c_str());
232    if (write_info_mutex.hasError() || !write_info_mutex.lock()) {
233      ALOGE("Unable to acquire the lock for writing %s! (%s)",
234            info_path.c_str(), write_info_mutex.getErrorMessage().c_str());
235      return Compiler::kErrInvalidSource;
236    }
237
238    // Perform the write.
239    if (!info->write(info_file)) {
240      ALOGE("Failed to sync the RS info file %s!", info_path.c_str());
241      return Compiler::kErrInvalidSource;
242    }
243  }
244
245  return Compiler::kSuccess;
246}
247
248bool RSCompilerDriver::build(BCCContext &pContext,
249                             const char *pCacheDir,
250                             const char *pResName,
251                             const char *pBitcode,
252                             size_t pBitcodeSize,
253                             const char *commandLine,
254                             const char *pRuntimePath,
255                             RSLinkRuntimeCallback pLinkRuntimeCallback,
256                             bool pDumpIR) {
257    //  android::StopWatch build_time("bcc: RSCompilerDriver::build time");
258  //===--------------------------------------------------------------------===//
259  // Check parameters.
260  //===--------------------------------------------------------------------===//
261  if ((pCacheDir == nullptr) || (pResName == nullptr)) {
262    ALOGE("Invalid parameter passed to RSCompilerDriver::build()! (cache dir: "
263          "%s, resource name: %s)", ((pCacheDir) ? pCacheDir : "(null)"),
264                                    ((pResName) ? pResName : "(null)"));
265    return false;
266  }
267
268  if ((pBitcode == nullptr) || (pBitcodeSize <= 0)) {
269    ALOGE("No bitcode supplied! (bitcode: %p, size of bitcode: %u)",
270          pBitcode, static_cast<unsigned>(pBitcodeSize));
271    return false;
272  }
273
274  //===--------------------------------------------------------------------===//
275  // Prepare dependency information.
276  //===--------------------------------------------------------------------===//
277  uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH];
278  Sha1Util::GetSHA1DigestFromBuffer(bitcode_sha1, pBitcode, pBitcodeSize);
279
280  //===--------------------------------------------------------------------===//
281  // Construct output path.
282  // {pCacheDir}/{pResName}.o
283  //===--------------------------------------------------------------------===//
284  llvm::SmallString<80> output_path(pCacheDir);
285  llvm::sys::path::append(output_path, pResName);
286  llvm::sys::path::replace_extension(output_path, ".o");
287
288  //===--------------------------------------------------------------------===//
289  // Load the bitcode and create script.
290  //===--------------------------------------------------------------------===//
291  Source *source = Source::CreateFromBuffer(pContext, pResName,
292                                            pBitcode, pBitcodeSize);
293  if (source == nullptr) {
294    return false;
295  }
296
297  RSScript script(*source);
298  if (pLinkRuntimeCallback) {
299    setLinkRuntimeCallback(pLinkRuntimeCallback);
300  }
301
302  script.setLinkRuntimeCallback(getLinkRuntimeCallback());
303
304  // Read information from bitcode wrapper.
305  bcinfo::BitcodeWrapper wrapper(pBitcode, pBitcodeSize);
306  script.setCompilerVersion(wrapper.getCompilerVersion());
307  script.setOptimizationLevel(static_cast<RSScript::OptimizationLevel>(
308                              wrapper.getOptimizationLevel()));
309
310  //===--------------------------------------------------------------------===//
311  // Compile the script
312  //===--------------------------------------------------------------------===//
313  Compiler::ErrorCode status = compileScript(script, pResName,
314                                             output_path.c_str(),
315                                             pRuntimePath, bitcode_sha1, commandLine,
316                                             true, pDumpIR);
317
318  return status == Compiler::kSuccess;
319}
320
321bool RSCompilerDriver::buildScriptGroup(
322    BCCContext& Context, const char* pOutputFilepath, const char*pRuntimePath,
323    const std::vector<const Source*>& sources, const std::vector<int>& slots,
324    bool dumpIR) {
325  llvm::Module* module = fuseKernels(Context, sources, slots);
326  if (module == nullptr) {
327    return false;
328  }
329
330  const std::unique_ptr<Source> source(
331      Source::CreateFromModule(Context, pOutputFilepath, *module));
332  RSScript script(*source);
333
334  uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH];
335  const char* compileCommandLineToEmbed = "";
336
337  llvm::SmallString<80> output_path(pOutputFilepath);
338  llvm::sys::path::replace_extension(output_path, ".o");
339
340  compileScript(script, pOutputFilepath, output_path.c_str(), pRuntimePath,
341                bitcode_sha1, compileCommandLineToEmbed, true, dumpIR);
342
343  return true;
344}
345
346bool RSCompilerDriver::buildForCompatLib(RSScript &pScript, const char *pOut,
347                                         const char *pRuntimePath) {
348  // For compat lib, we don't check the RS info file so we don't need the source hash,
349  // compile command, and build fingerprint.
350  // TODO We may want to make them optional or embed real values.
351  uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH] = {0};
352  const char* compileCommandLineToEmbed = "";
353  const char* buildFingerprintToEmbed = "";
354
355  RSInfo* info = RSInfo::ExtractFromSource(pScript.getSource(), bitcode_sha1,
356                                           compileCommandLineToEmbed, buildFingerprintToEmbed);
357  if (info == nullptr) {
358    return false;
359  }
360  pScript.setInfo(info);
361
362  // Embed the info string directly in the ELF, since this path is for an
363  // offline (host) compilation.
364  pScript.setEmbedInfo(true);
365
366  Compiler::ErrorCode status = compileScript(pScript, pOut, pOut, pRuntimePath, bitcode_sha1,
367                                             compileCommandLineToEmbed, false, false);
368  if (status != Compiler::kSuccess) {
369    return false;
370  }
371
372  return true;
373}
374