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