1/* 2 * Copyright 2010-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 "clang/Basic/DiagnosticOptions.h" 18#include "clang/Driver/DriverDiagnostic.h" 19#include "clang/Driver/Options.h" 20#include "clang/Frontend/CompilerInvocation.h" 21#include "clang/Frontend/FrontendDiagnostic.h" 22#include "clang/Frontend/TextDiagnosticPrinter.h" 23#include "clang/Frontend/Utils.h" 24 25#include "llvm/ADT/SmallVector.h" 26#include "llvm/ADT/IntrusiveRefCntPtr.h" 27 28#include "llvm/Option/OptTable.h" 29#include "llvm/Support/ManagedStatic.h" 30#include "llvm/Support/MemoryBuffer.h" 31#include "llvm/Support/Path.h" 32#include "llvm/Support/raw_ostream.h" 33#include "llvm/Support/Signals.h" 34#include "llvm/Target/TargetMachine.h" 35 36#include "rs_cc_options.h" 37#include "slang.h" 38#include "slang_assert.h" 39#include "slang_diagnostic_buffer.h" 40#include "slang_rs_reflect_utils.h" 41 42#include <list> 43#include <set> 44#include <string> 45 46namespace { 47class StringSet : public llvm::cl::StringSaver { 48public: 49 const char *SaveString(const char *Str) override { 50 return Strings.insert(Str).first->c_str(); 51 } 52 53private: 54 std::set<std::string> Strings; 55}; 56} 57 58static const char *DetermineOutputFile(const std::string &OutputDir, 59 const std::string &PathSuffix, 60 const char *InputFile, 61 slang::Slang::OutputType OutputType, 62 StringSet *SavedStrings) { 63 if (OutputType == slang::Slang::OT_Nothing) 64 return "/dev/null"; 65 66 std::string OutputFile(OutputDir); 67 68 // Append '/' to Opts.mBitcodeOutputDir if not presents 69 if (!OutputFile.empty() && 70 (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR) 71 OutputFile.append(1, OS_PATH_SEPARATOR); 72 73 if (!PathSuffix.empty()) { 74 OutputFile.append(PathSuffix); 75 OutputFile.append(1, OS_PATH_SEPARATOR); 76 } 77 78 if (OutputType == slang::Slang::OT_Dependency) { 79 // The build system wants the .d file name stem to be exactly the same as 80 // the source .rs file, instead of the .bc file. 81 OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile)); 82 } else { 83 OutputFile.append( 84 slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile)); 85 } 86 87 switch (OutputType) { 88 case slang::Slang::OT_Dependency: { 89 OutputFile.append(".d"); 90 break; 91 } 92 case slang::Slang::OT_Assembly: { 93 OutputFile.append(".S"); 94 break; 95 } 96 case slang::Slang::OT_LLVMAssembly: { 97 OutputFile.append(".ll"); 98 break; 99 } 100 case slang::Slang::OT_Object: { 101 OutputFile.append(".o"); 102 break; 103 } 104 case slang::Slang::OT_Bitcode: { 105 OutputFile.append(".bc"); 106 break; 107 } 108 case slang::Slang::OT_Nothing: 109 default: { 110 slangAssert(false && "Invalid output type!"); 111 } 112 } 113 114 return SavedStrings->SaveString(OutputFile.c_str()); 115} 116 117typedef std::list<std::pair<const char*, const char*> > NamePairList; 118 119/* 120 * Compile the Inputs. 121 * 122 * Returns 0 on success and nonzero on failure. 123 * 124 * IOFiles - list of (foo.rs, foo.bc) pairs of input/output files. 125 * IOFiles32 - list of input/output pairs for 32-bit compilation. 126 * Inputs - input filenames. 127 * Opts - options controlling compilation. 128 * DiagEngine - Clang diagnostic engine (for creating diagnostics). 129 * DiagClient - Slang diagnostic consumer (collects and displays diagnostics). 130 * SavedStrings - expanded strings copied from argv source input files. 131 * 132 * We populate IOFiles dynamically while working through the list of Inputs. 133 * On any 64-bit compilation, we pass back in the 32-bit pairs of files as 134 * IOFiles32. This allows the 64-bit compiler to later bundle up both the 135 * 32-bit and 64-bit bitcode outputs to be included in the final reflected 136 * source code that is emitted. 137 */ 138static void makeFileList(NamePairList *IOFiles, NamePairList *DepFiles, 139 const llvm::SmallVector<const char*, 16> &Inputs, slang::RSCCOptions &Opts, 140 StringSet *SavedStrings) { 141 std::string PathSuffix = ""; 142 // In our mixed 32/64-bit path, we need to suffix our files differently for 143 // both 32-bit and 64-bit versions. 144 if (Opts.mEmit3264) { 145 if (Opts.mBitWidth == 64) { 146 PathSuffix = "bc64"; 147 } else { 148 PathSuffix = "bc32"; 149 } 150 } 151 152 for (int i = 0, e = Inputs.size(); i != e; i++) { 153 const char *InputFile = Inputs[i]; 154 155 const char *BCOutputFile = DetermineOutputFile(Opts.mBitcodeOutputDir, 156 PathSuffix, InputFile, 157 Opts.mOutputType, 158 SavedStrings); 159 const char *OutputFile = BCOutputFile; 160 161 if (Opts.mEmitDependency) { 162 // The dependency file is always emitted without a PathSuffix. 163 // Collisions between 32-bit and 64-bit files don't make a difference, 164 // because they share the same sources/dependencies. 165 const char *DepOutputFile = 166 DetermineOutputFile(Opts.mDependencyOutputDir, "", InputFile, 167 slang::Slang::OT_Dependency, SavedStrings); 168 if (Opts.mOutputType == slang::Slang::OT_Dependency) { 169 OutputFile = DepOutputFile; 170 } 171 172 DepFiles->push_back(std::make_pair(BCOutputFile, DepOutputFile)); 173 } 174 175 IOFiles->push_back(std::make_pair(InputFile, OutputFile)); 176 } 177} 178 179#define str(s) #s 180#define wrap_str(s) str(s) 181static void llvm_rs_cc_VersionPrinter() { 182 llvm::raw_ostream &OS = llvm::outs(); 183 OS << "llvm-rs-cc: Renderscript compiler\n" 184 << " (http://developer.android.com/guide/topics/renderscript)\n" 185 << " based on LLVM (http://llvm.org):\n"; 186 OS << " Built " << __DATE__ << " (" << __TIME__ ").\n"; 187 OS << " Target APIs: " << SLANG_MINIMUM_TARGET_API << " - " 188 << SLANG_MAXIMUM_TARGET_API; 189 OS << "\n Build type: " << wrap_str(TARGET_BUILD_VARIANT); 190#ifndef __DISABLE_ASSERTS 191 OS << " with assertions"; 192#endif 193 OS << ".\n"; 194} 195#undef wrap_str 196#undef str 197 198static void LLVMErrorHandler(void *UserData, const std::string &Message, 199 bool GenCrashDialog) { 200 clang::DiagnosticsEngine *DiagEngine = 201 static_cast<clang::DiagnosticsEngine *>(UserData); 202 203 DiagEngine->Report(clang::diag::err_fe_error_backend) << Message; 204 205 // Run the interrupt handlers to make sure any special cleanups get done, in 206 // particular that we remove files registered with RemoveFileOnSignal. 207 llvm::sys::RunInterruptHandlers(); 208 209 exit(1); 210} 211 212int main(int argc, const char **argv) { 213 llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 214 LLVMInitializeARMTargetInfo(); 215 LLVMInitializeARMTarget(); 216 LLVMInitializeARMAsmPrinter(); 217 218 StringSet SavedStrings; // Keeps track of strings to be destroyed at the end. 219 220 // Parse the command line arguments and respond to show help & version 221 // commands. 222 llvm::SmallVector<const char *, 16> Inputs; 223 slang::RSCCOptions Opts; 224 llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts = 225 new clang::DiagnosticOptions(); 226 if (!slang::ParseArguments(llvm::makeArrayRef(argv, argc), Inputs, Opts, 227 *DiagOpts, SavedStrings)) { 228 // Exits when there's any error occurred during parsing the arguments 229 return 1; 230 } 231 if (Opts.mShowHelp) { 232 std::unique_ptr<llvm::opt::OptTable> OptTbl(slang::createRSCCOptTable()); 233 const std::string Argv0 = llvm::sys::path::stem(argv[0]); 234 OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(), 235 "Renderscript source compiler"); 236 return 0; 237 } 238 if (Opts.mShowVersion) { 239 llvm_rs_cc_VersionPrinter(); 240 return 0; 241 } 242 243 // Initialize the diagnostic objects 244 llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs( 245 new clang::DiagnosticIDs()); 246 slang::DiagnosticBuffer DiagsBuffer; 247 clang::DiagnosticsEngine DiagEngine(DiagIDs, &*DiagOpts, &DiagsBuffer, false); 248 clang::ProcessWarningOptions(DiagEngine, *DiagOpts); 249 (void)DiagEngine.setSeverityForGroup(clang::diag::Flavor::WarningOrError, 250 "implicit-function-declaration", 251 clang::diag::Severity::Error); 252 253 // Report error if no input file 254 if (Inputs.empty()) { 255 DiagEngine.Report(clang::diag::err_drv_no_input_files); 256 llvm::errs() << DiagsBuffer.str(); 257 return 1; 258 } 259 260 llvm::install_fatal_error_handler(LLVMErrorHandler, &DiagEngine); 261 262 // Compile the 32 bit version 263 NamePairList IOFiles32; 264 NamePairList DepFiles32; 265 makeFileList(&IOFiles32, &DepFiles32, Inputs, Opts, &SavedStrings); 266 267 int CompileFailed = 0; 268 // Handle 32-bit case for Java and C++ reflection. 269 // For Java, both 32bit and 64bit will be generated. 270 // For C++, either 64bit or 32bit will be generated based on the target. 271 if (Opts.mEmit3264 || Opts.mBitWidth == 32) { 272 std::unique_ptr<slang::Slang> Compiler( 273 new slang::Slang(32, &DiagEngine, &DiagsBuffer)); 274 CompileFailed = 275 !Compiler->compile(IOFiles32, IOFiles32, DepFiles32, Opts, *DiagOpts); 276 } 277 278 // Handle the 64-bit case too! 279 bool needEmit64 = Opts.mEmit3264 || Opts.mBitWidth == 64; 280 if (needEmit64 && !CompileFailed) { 281 Opts.mBitWidth = 64; 282 NamePairList IOFiles64; 283 NamePairList DepFiles64; 284 makeFileList(&IOFiles64, &DepFiles64, Inputs, Opts, &SavedStrings); 285 286 std::unique_ptr<slang::Slang> Compiler( 287 new slang::Slang(64, &DiagEngine, &DiagsBuffer)); 288 CompileFailed = 289 !Compiler->compile(IOFiles64, IOFiles32, DepFiles64, Opts, *DiagOpts); 290 } 291 292 llvm::errs() << DiagsBuffer.str(); 293 llvm::remove_fatal_error_handler(); 294 return CompileFailed; 295} 296