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