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 
46 namespace {
47 class StringSet : public llvm::cl::StringSaver {
48 public:
SaveString(const char * Str)49   const char *SaveString(const char *Str) override {
50     return Strings.insert(Str).first->c_str();
51   }
52 
53 private:
54   std::set<std::string> Strings;
55 };
56 }
57 
DetermineOutputFile(const std::string & OutputDir,const std::string & PathSuffix,const char * InputFile,slang::Slang::OutputType OutputType,StringSet * SavedStrings)58 static 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 
117 typedef 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  */
makeFileList(NamePairList * IOFiles,NamePairList * DepFiles,const llvm::SmallVector<const char *,16> & Inputs,slang::RSCCOptions & Opts,StringSet * SavedStrings)138 static 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)
llvm_rs_cc_VersionPrinter()181 static 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 
LLVMErrorHandler(void * UserData,const std::string & Message,bool GenCrashDialog)198 static 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 
main(int argc,const char ** argv)212 int 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