You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
11 KiB
322 lines
11 KiB
/*
|
|
* Copyright 2010-2012, The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "clang/Basic/AllDiagnostics.h"
|
|
#include "clang/Basic/DiagnosticOptions.h"
|
|
#include "clang/Driver/DriverDiagnostic.h"
|
|
#include "clang/Driver/Options.h"
|
|
#include "clang/Frontend/CompilerInvocation.h"
|
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
|
#include "clang/Frontend/Utils.h"
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
|
|
|
#include "llvm/Option/OptTable.h"
|
|
#include "llvm/Support/Allocator.h"
|
|
#include "llvm/Support/ManagedStatic.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/Support/Signals.h"
|
|
#include "llvm/Support/StringSaver.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include "llvm/Target/TargetMachine.h"
|
|
|
|
#include "os_sep.h"
|
|
#include "rs_cc_options.h"
|
|
#include "slang.h"
|
|
#include "slang_assert.h"
|
|
#include "slang_diagnostic_buffer.h"
|
|
#include "slang_rs_reflect_utils.h"
|
|
#include "slang_rs_reflection_state.h"
|
|
|
|
#include <list>
|
|
#include <set>
|
|
#include <string>
|
|
|
|
namespace {
|
|
class StringSet {
|
|
public:
|
|
const char *save(const char *Str) {
|
|
return Strings.save(Str);
|
|
}
|
|
|
|
StringSet() : Strings(A), A() {}
|
|
|
|
llvm::StringSaver & getStringSaver() { return Strings; }
|
|
|
|
private:
|
|
llvm::StringSaver Strings;
|
|
llvm::BumpPtrAllocator A;
|
|
};
|
|
}
|
|
|
|
static const char *DetermineOutputFile(const std::string &OutputDir,
|
|
const std::string &PathSuffix,
|
|
const char *InputFile,
|
|
slang::Slang::OutputType OutputType,
|
|
StringSet *SavedStrings) {
|
|
if (OutputType == slang::Slang::OT_Nothing)
|
|
return "/dev/null";
|
|
|
|
std::string OutputFile(OutputDir);
|
|
|
|
// Append '/' to Opts.mBitcodeOutputDir if not presents
|
|
if (!OutputFile.empty() &&
|
|
(OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
|
|
OutputFile.append(1, OS_PATH_SEPARATOR);
|
|
|
|
if (!PathSuffix.empty()) {
|
|
OutputFile.append(PathSuffix);
|
|
OutputFile.append(1, OS_PATH_SEPARATOR);
|
|
}
|
|
|
|
if (OutputType == slang::Slang::OT_Dependency) {
|
|
// The build system wants the .d file name stem to be exactly the same as
|
|
// the source .rs file, instead of the .bc file.
|
|
OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
|
|
} else {
|
|
OutputFile.append(
|
|
slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
|
|
}
|
|
|
|
switch (OutputType) {
|
|
case slang::Slang::OT_Dependency: {
|
|
OutputFile.append(".d");
|
|
break;
|
|
}
|
|
case slang::Slang::OT_Assembly: {
|
|
OutputFile.append(".S");
|
|
break;
|
|
}
|
|
case slang::Slang::OT_LLVMAssembly: {
|
|
OutputFile.append(".ll");
|
|
break;
|
|
}
|
|
case slang::Slang::OT_Object: {
|
|
OutputFile.append(".o");
|
|
break;
|
|
}
|
|
case slang::Slang::OT_Bitcode: {
|
|
OutputFile.append(".bc");
|
|
break;
|
|
}
|
|
case slang::Slang::OT_Nothing:
|
|
default: {
|
|
slangAssert(false && "Invalid output type!");
|
|
}
|
|
}
|
|
|
|
return SavedStrings->save(OutputFile.c_str());
|
|
}
|
|
|
|
typedef std::list<std::pair<const char*, const char*> > NamePairList;
|
|
|
|
/*
|
|
* Compile the Inputs.
|
|
*
|
|
* Returns 0 on success and nonzero on failure.
|
|
*
|
|
* IOFiles - list of (foo.rs, foo.bc) pairs of input/output files.
|
|
* IOFiles32 - list of input/output pairs for 32-bit compilation.
|
|
* Inputs - input filenames.
|
|
* Opts - options controlling compilation.
|
|
* DiagEngine - Clang diagnostic engine (for creating diagnostics).
|
|
* DiagClient - Slang diagnostic consumer (collects and displays diagnostics).
|
|
* SavedStrings - expanded strings copied from argv source input files.
|
|
*
|
|
* We populate IOFiles dynamically while working through the list of Inputs.
|
|
* On any 64-bit compilation, we pass back in the 32-bit pairs of files as
|
|
* IOFiles32. This allows the 64-bit compiler to later bundle up both the
|
|
* 32-bit and 64-bit bitcode outputs to be included in the final reflected
|
|
* source code that is emitted.
|
|
*/
|
|
static void makeFileList(NamePairList *IOFiles, NamePairList *DepFiles,
|
|
const llvm::SmallVector<const char*, 16> &Inputs, slang::RSCCOptions &Opts,
|
|
StringSet *SavedStrings) {
|
|
std::string PathSuffix = "";
|
|
// In our mixed 32/64-bit path, we need to suffix our files differently for
|
|
// both 32-bit and 64-bit versions.
|
|
if (Opts.mEmit3264) {
|
|
if (Opts.mBitWidth == 64) {
|
|
PathSuffix = "bc64";
|
|
} else {
|
|
PathSuffix = "bc32";
|
|
}
|
|
}
|
|
|
|
for (int i = 0, e = Inputs.size(); i != e; i++) {
|
|
const char *InputFile = Inputs[i];
|
|
|
|
const char *BCOutputFile = DetermineOutputFile(Opts.mBitcodeOutputDir,
|
|
PathSuffix, InputFile,
|
|
Opts.mOutputType,
|
|
SavedStrings);
|
|
const char *OutputFile = BCOutputFile;
|
|
|
|
if (Opts.mEmitDependency) {
|
|
// The dependency file is always emitted without a PathSuffix.
|
|
// Collisions between 32-bit and 64-bit files don't make a difference,
|
|
// because they share the same sources/dependencies.
|
|
const char *DepOutputFile =
|
|
DetermineOutputFile(Opts.mDependencyOutputDir, "", InputFile,
|
|
slang::Slang::OT_Dependency, SavedStrings);
|
|
if (Opts.mOutputType == slang::Slang::OT_Dependency) {
|
|
OutputFile = DepOutputFile;
|
|
}
|
|
|
|
DepFiles->push_back(std::make_pair(BCOutputFile, DepOutputFile));
|
|
}
|
|
|
|
IOFiles->push_back(std::make_pair(InputFile, OutputFile));
|
|
}
|
|
}
|
|
|
|
#define str(s) #s
|
|
#define wrap_str(s) str(s)
|
|
static void llvm_rs_cc_VersionPrinter() {
|
|
llvm::raw_ostream &OS = llvm::outs();
|
|
OS << "llvm-rs-cc: Renderscript compiler\n"
|
|
<< " (http://developer.android.com/guide/topics/renderscript)\n"
|
|
<< " based on LLVM (http://llvm.org):\n";
|
|
OS << " Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
|
|
<< SLANG_MAXIMUM_TARGET_API;
|
|
OS << "\n Build type: " << wrap_str(TARGET_BUILD_VARIANT);
|
|
#ifndef __DISABLE_ASSERTS
|
|
OS << " with assertions";
|
|
#endif
|
|
OS << ".\n";
|
|
}
|
|
#undef wrap_str
|
|
#undef str
|
|
|
|
static void LLVMErrorHandler(void *UserData, const std::string &Message,
|
|
bool GenCrashDialog) {
|
|
clang::DiagnosticsEngine *DiagEngine =
|
|
static_cast<clang::DiagnosticsEngine *>(UserData);
|
|
|
|
DiagEngine->Report(clang::diag::err_fe_error_backend) << Message;
|
|
|
|
// Run the interrupt handlers to make sure any special cleanups get done, in
|
|
// particular that we remove files registered with RemoveFileOnSignal.
|
|
llvm::sys::RunInterruptHandlers();
|
|
|
|
exit(1);
|
|
}
|
|
|
|
// TODO(b/37755219): detect leaks
|
|
extern "C" const char *__asan_default_options() {
|
|
return "detect_leaks=0";
|
|
}
|
|
|
|
static void emitDeprecationWarning(clang::DiagnosticsEngine *DiagEngine) {
|
|
DiagEngine->Report(clang::diag::warn_deprecated_message)
|
|
<< "Renderscript"
|
|
<< "Please refer to the migration guide "
|
|
"(https://developer.android.com/guide/topics/renderscript/migration-guide) "
|
|
"for the proposed alternatives.";
|
|
}
|
|
|
|
int main(int argc, const char **argv) {
|
|
llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
|
|
LLVMInitializeARMTargetInfo();
|
|
LLVMInitializeARMTarget();
|
|
LLVMInitializeARMAsmPrinter();
|
|
|
|
StringSet SavedStrings; // Keeps track of strings to be destroyed at the end.
|
|
|
|
// Parse the command line arguments and respond to show help & version
|
|
// commands.
|
|
llvm::SmallVector<const char *, 16> Inputs;
|
|
slang::RSCCOptions Opts;
|
|
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts =
|
|
new clang::DiagnosticOptions();
|
|
if (!slang::ParseArguments(llvm::makeArrayRef(argv, argc), Inputs, Opts,
|
|
*DiagOpts, SavedStrings.getStringSaver())) {
|
|
// Exits when there's any error occurred during parsing the arguments
|
|
return 1;
|
|
}
|
|
if (Opts.mShowHelp) {
|
|
std::unique_ptr<llvm::opt::OptTable> OptTbl(slang::createRSCCOptTable());
|
|
const std::string Argv0 = llvm::sys::path::stem(argv[0]);
|
|
OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
|
|
"Renderscript source compiler");
|
|
return 0;
|
|
}
|
|
if (Opts.mShowVersion) {
|
|
llvm_rs_cc_VersionPrinter();
|
|
return 0;
|
|
}
|
|
|
|
// Initialize the diagnostic objects
|
|
llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
|
|
new clang::DiagnosticIDs());
|
|
slang::DiagnosticBuffer DiagsBuffer;
|
|
clang::DiagnosticsEngine DiagEngine(DiagIDs, &*DiagOpts, &DiagsBuffer, false);
|
|
clang::ProcessWarningOptions(DiagEngine, *DiagOpts);
|
|
(void)DiagEngine.setSeverityForGroup(clang::diag::Flavor::WarningOrError,
|
|
"implicit-function-declaration",
|
|
clang::diag::Severity::Error);
|
|
emitDeprecationWarning(&DiagEngine);
|
|
|
|
// Report error if no input file
|
|
if (Inputs.empty()) {
|
|
DiagEngine.Report(clang::diag::err_drv_no_input_files);
|
|
llvm::errs() << DiagsBuffer.str();
|
|
return 1;
|
|
}
|
|
|
|
llvm::install_fatal_error_handler(LLVMErrorHandler, &DiagEngine);
|
|
|
|
slang::ReflectionState Reflection;
|
|
|
|
// Compile the 32 bit version
|
|
NamePairList IOFiles32;
|
|
NamePairList DepFiles32;
|
|
makeFileList(&IOFiles32, &DepFiles32, Inputs, Opts, &SavedStrings);
|
|
|
|
int CompileFailed = 0;
|
|
// Handle 32-bit case for Java and C++ reflection.
|
|
// For Java, both 32bit and 64bit will be generated.
|
|
// For C++, either 64bit or 32bit will be generated based on the target.
|
|
if (Opts.mEmit3264 || Opts.mBitWidth == 32) {
|
|
std::unique_ptr<slang::Slang> Compiler(
|
|
new slang::Slang(32, &DiagEngine, &DiagsBuffer));
|
|
CompileFailed =
|
|
!Compiler->compile(IOFiles32, IOFiles32, DepFiles32, Opts, *DiagOpts, &Reflection);
|
|
}
|
|
|
|
// Handle the 64-bit case too!
|
|
bool needEmit64 = Opts.mEmit3264 || Opts.mBitWidth == 64;
|
|
if (needEmit64 && !CompileFailed) {
|
|
Opts.mBitWidth = 64;
|
|
NamePairList IOFiles64;
|
|
NamePairList DepFiles64;
|
|
makeFileList(&IOFiles64, &DepFiles64, Inputs, Opts, &SavedStrings);
|
|
|
|
std::unique_ptr<slang::Slang> Compiler(
|
|
new slang::Slang(64, &DiagEngine, &DiagsBuffer));
|
|
CompileFailed =
|
|
!Compiler->compile(IOFiles64, IOFiles32, DepFiles64, Opts, *DiagOpts, &Reflection);
|
|
}
|
|
|
|
llvm::errs() << DiagsBuffer.str();
|
|
llvm::remove_fatal_error_handler();
|
|
return CompileFailed;
|
|
}
|