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/TextDiagnosticPrinter.h"
21 #include "clang/Frontend/Utils.h"
22 
23 #include "llvm/ADT/SmallVector.h"
24 #include "llvm/ADT/IntrusiveRefCntPtr.h"
25 
26 #include "llvm/Option/OptTable.h"
27 #include "llvm/Support/CommandLine.h"
28 #include "llvm/Support/ManagedStatic.h"
29 #include "llvm/Support/MemoryBuffer.h"
30 #include "llvm/Support/Path.h"
31 #include "llvm/Support/raw_ostream.h"
32 #include "llvm/Target/TargetMachine.h"
33 
34 #include "rs_cc_options.h"
35 #include "slang.h"
36 #include "slang_assert.h"
37 #include "slang_diagnostic_buffer.h"
38 #include "slang_rs.h"
39 #include "slang_rs_reflect_utils.h"
40 
41 #include <list>
42 #include <set>
43 #include <string>
44 
45 // SaveStringInSet, ExpandArgsFromBuf and ExpandArgv are all copied from
46 // $(CLANG_ROOT)/tools/driver/driver.cpp for processing argc/argv passed in
47 // main().
SaveStringInSet(std::set<std::string> & SavedStrings,llvm::StringRef S)48 static inline const char *SaveStringInSet(std::set<std::string> &SavedStrings,
49                                           llvm::StringRef S) {
50   return SavedStrings.insert(S).first->c_str();
51 }
52 static void ExpandArgsFromBuf(const char *Arg,
53                               llvm::SmallVectorImpl<const char*> &ArgVector,
54                               std::set<std::string> &SavedStrings);
55 static void ExpandArgv(int argc, const char **argv,
56                        llvm::SmallVectorImpl<const char*> &ArgVector,
57                        std::set<std::string> &SavedStrings);
58 
DetermineOutputFile(const std::string & OutputDir,const std::string & PathSuffix,const char * InputFile,slang::Slang::OutputType OutputType,std::set<std::string> & SavedStrings)59 static const char *DetermineOutputFile(const std::string &OutputDir,
60                                        const std::string &PathSuffix,
61                                        const char *InputFile,
62                                        slang::Slang::OutputType OutputType,
63                                        std::set<std::string> &SavedStrings) {
64   if (OutputType == slang::Slang::OT_Nothing)
65     return "/dev/null";
66 
67   std::string OutputFile(OutputDir);
68 
69   // Append '/' to Opts.mBitcodeOutputDir if not presents
70   if (!OutputFile.empty() &&
71       (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
72     OutputFile.append(1, OS_PATH_SEPARATOR);
73 
74   if (!PathSuffix.empty()) {
75     OutputFile.append(PathSuffix);
76     OutputFile.append(1, OS_PATH_SEPARATOR);
77   }
78 
79   if (OutputType == slang::Slang::OT_Dependency) {
80     // The build system wants the .d file name stem to be exactly the same as
81     // the source .rs file, instead of the .bc file.
82     OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
83   } else {
84     OutputFile.append(
85         slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
86   }
87 
88   switch (OutputType) {
89     case slang::Slang::OT_Dependency: {
90       OutputFile.append(".d");
91       break;
92     }
93     case slang::Slang::OT_Assembly: {
94       OutputFile.append(".S");
95       break;
96     }
97     case slang::Slang::OT_LLVMAssembly: {
98       OutputFile.append(".ll");
99       break;
100     }
101     case slang::Slang::OT_Object: {
102       OutputFile.append(".o");
103       break;
104     }
105     case slang::Slang::OT_Bitcode: {
106       OutputFile.append(".bc");
107       break;
108     }
109     case slang::Slang::OT_Nothing:
110     default: {
111       slangAssert(false && "Invalid output type!");
112     }
113   }
114 
115   return SaveStringInSet(SavedStrings, OutputFile);
116 }
117 
118 typedef std::list<std::pair<const char*, const char*> > NamePairList;
119 
120 /*
121  * Compile the Inputs.
122  *
123  * Returns 0 on success and nonzero on failure.
124  *
125  * IOFiles - list of (foo.rs, foo.bc) pairs of input/output files.
126  * IOFiles32 - list of input/output pairs for 32-bit compilation.
127  * Inputs - input filenames.
128  * Opts - options controlling compilation.
129  * DiagEngine - Clang diagnostic engine (for creating diagnostics).
130  * DiagClient - Slang diagnostic consumer (collects and displays diagnostics).
131  * SavedStrings - expanded strings copied from argv source input files.
132  *
133  * We populate IOFiles dynamically while working through the list of Inputs.
134  * On any 64-bit compilation, we pass back in the 32-bit pairs of files as
135  * IOFiles32. This allows the 64-bit compiler to later bundle up both the
136  * 32-bit and 64-bit bitcode outputs to be included in the final reflected
137  * source code that is emitted.
138  */
compileFiles(NamePairList * IOFiles,NamePairList * IOFiles32,const llvm::SmallVector<const char *,16> & Inputs,slang::RSCCOptions & Opts,clang::DiagnosticsEngine * DiagEngine,slang::DiagnosticBuffer * DiagClient,std::set<std::string> * SavedStrings)139 static int compileFiles(NamePairList *IOFiles, NamePairList *IOFiles32,
140     const llvm::SmallVector<const char*, 16> &Inputs, slang::RSCCOptions &Opts,
141     clang::DiagnosticsEngine *DiagEngine, slang::DiagnosticBuffer *DiagClient,
142     std::set<std::string> *SavedStrings) {
143   NamePairList DepFiles;
144   std::string PathSuffix = "";
145   bool CompileSecondTimeFor64Bit = false;
146 
147   // In our mixed 32/64-bit path, we need to suffix our files differently for
148   // both 32-bit and 64-bit versions.
149   if (Opts.mEmit3264) {
150     if (Opts.mBitWidth == 64) {
151       PathSuffix = "bc64";
152       CompileSecondTimeFor64Bit = true;
153     } else {
154       PathSuffix = "bc32";
155     }
156   }
157 
158   for (int i = 0, e = Inputs.size(); i != e; i++) {
159     const char *InputFile = Inputs[i];
160 
161     const char *BCOutputFile = DetermineOutputFile(Opts.mBitcodeOutputDir,
162                                                    PathSuffix, InputFile,
163                                                    slang::Slang::OT_Bitcode,
164                                                    *SavedStrings);
165     const char *OutputFile = BCOutputFile;
166 
167     if (Opts.mEmitDependency) {
168       // The dependency file is always emitted without a PathSuffix.
169       // Collisions between 32-bit and 64-bit files don't make a difference,
170       // because they share the same sources/dependencies.
171       const char *DepOutputFile =
172           DetermineOutputFile(Opts.mDependencyOutputDir, "", InputFile,
173                               slang::Slang::OT_Dependency, *SavedStrings);
174       if (Opts.mOutputType == slang::Slang::OT_Dependency) {
175         OutputFile = DepOutputFile;
176       }
177 
178       DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile));
179     }
180 
181     IOFiles->push_back(std::make_pair(InputFile, OutputFile));
182   }
183 
184   std::unique_ptr<slang::SlangRS> Compiler(new slang::SlangRS());
185   Compiler->init(Opts.mBitWidth, DiagEngine, DiagClient);
186   int CompileFailed = !Compiler->compile(*IOFiles, *IOFiles32, DepFiles, Opts);
187   // We suppress warnings (via reset) if we are doing a second compilation.
188   Compiler->reset(CompileSecondTimeFor64Bit);
189   return CompileFailed;
190 }
191 
192 #define str(s) #s
193 #define wrap_str(s) str(s)
llvm_rs_cc_VersionPrinter()194 static void llvm_rs_cc_VersionPrinter() {
195   llvm::raw_ostream &OS = llvm::outs();
196   OS << "llvm-rs-cc: Renderscript compiler\n"
197      << "  (http://developer.android.com/guide/topics/renderscript)\n"
198      << "  based on LLVM (http://llvm.org):\n";
199   OS << "  Built " << __DATE__ << " (" << __TIME__ ").\n";
200   OS << "  Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
201      << SLANG_MAXIMUM_TARGET_API;
202   OS << "\n  Build type: " << wrap_str(TARGET_BUILD_VARIANT);
203 #ifndef __DISABLE_ASSERTS
204   OS << " with assertions";
205 #endif
206   OS << ".\n";
207 }
208 #undef wrap_str
209 #undef str
210 
main(int argc,const char ** argv)211 int main(int argc, const char **argv) {
212   std::set<std::string> SavedStrings;
213   llvm::SmallVector<const char*, 256> ArgVector;
214   slang::RSCCOptions Opts;
215   llvm::SmallVector<const char*, 16> Inputs;
216   std::string Argv0;
217 
218   llvm::llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.
219 
220   ExpandArgv(argc, argv, ArgVector, SavedStrings);
221 
222   // Argv0
223   Argv0 = llvm::sys::path::stem(ArgVector[0]);
224 
225   // Setup diagnostic engine
226   slang::DiagnosticBuffer *DiagClient = new slang::DiagnosticBuffer();
227 
228   llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
229     new clang::DiagnosticIDs());
230 
231   llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts(
232     new clang::DiagnosticOptions());
233   clang::DiagnosticsEngine DiagEngine(DiagIDs, &*DiagOpts, DiagClient, true);
234 
235   slang::Slang::GlobalInitialization();
236 
237   slang::ParseArguments(ArgVector, Inputs, Opts, DiagEngine);
238 
239   // Exits when there's any error occurred during parsing the arguments
240   if (DiagEngine.hasErrorOccurred()) {
241     llvm::errs() << DiagClient->str();
242     return 1;
243   }
244 
245   if (Opts.mShowHelp) {
246     std::unique_ptr<llvm::opt::OptTable> OptTbl(slang::createRSCCOptTable());
247     OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
248                       "Renderscript source compiler");
249     return 0;
250   }
251 
252   if (Opts.mShowVersion) {
253     llvm_rs_cc_VersionPrinter();
254     return 0;
255   }
256 
257   // No input file
258   if (Inputs.empty()) {
259     DiagEngine.Report(clang::diag::err_drv_no_input_files);
260     llvm::errs() << DiagClient->str();
261     return 1;
262   }
263 
264   // Prepare input data for RS compiler.
265   NamePairList IOFiles64;
266   NamePairList IOFiles32;
267 
268   int CompileFailed = compileFiles(&IOFiles32, &IOFiles32, Inputs, Opts,
269                                    &DiagEngine, DiagClient, &SavedStrings);
270 
271   // Handle the 64-bit case too!
272   if (Opts.mEmit3264 && !CompileFailed) {
273     Opts.mBitWidth = 64;
274     CompileFailed = compileFiles(&IOFiles64, &IOFiles32, Inputs, Opts,
275                                  &DiagEngine, DiagClient, &SavedStrings);
276   }
277 
278   return CompileFailed;
279 }
280 
281 ///////////////////////////////////////////////////////////////////////////////
282 
283 // ExpandArgsFromBuf -
ExpandArgsFromBuf(const char * Arg,llvm::SmallVectorImpl<const char * > & ArgVector,std::set<std::string> & SavedStrings)284 static void ExpandArgsFromBuf(const char *Arg,
285                               llvm::SmallVectorImpl<const char*> &ArgVector,
286                               std::set<std::string> &SavedStrings) {
287   const char *FName = Arg + 1;
288   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MBOrErr =
289       llvm::MemoryBuffer::getFile(FName);
290   if (MBOrErr.getError()) {
291     // Unable to open the file
292     ArgVector.push_back(SaveStringInSet(SavedStrings, Arg));
293     return;
294   }
295   std::unique_ptr<llvm::MemoryBuffer> MemBuf = std::move(MBOrErr.get());
296 
297   const char *Buf = MemBuf->getBufferStart();
298   char InQuote = ' ';
299   std::string CurArg;
300 
301   for (const char *P = Buf; ; ++P) {
302     if (*P == '\0' || (isspace(*P) && InQuote == ' ')) {
303       if (!CurArg.empty()) {
304         if (CurArg[0] != '@') {
305           ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg));
306         } else {
307           ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings);
308         }
309 
310         CurArg = "";
311       }
312       if (*P == '\0')
313         break;
314       else
315         continue;
316     }
317 
318     if (isspace(*P)) {
319       if (InQuote != ' ')
320         CurArg.push_back(*P);
321       continue;
322     }
323 
324     if (*P == '"' || *P == '\'') {
325       if (InQuote == *P)
326         InQuote = ' ';
327       else if (InQuote == ' ')
328         InQuote = *P;
329       else
330         CurArg.push_back(*P);
331       continue;
332     }
333 
334     if (*P == '\\') {
335       ++P;
336       if (*P != '\0')
337         CurArg.push_back(*P);
338       continue;
339     }
340     CurArg.push_back(*P);
341   }
342 }
343 
344 // ExpandArgsFromBuf -
ExpandArgv(int argc,const char ** argv,llvm::SmallVectorImpl<const char * > & ArgVector,std::set<std::string> & SavedStrings)345 static void ExpandArgv(int argc, const char **argv,
346                        llvm::SmallVectorImpl<const char*> &ArgVector,
347                        std::set<std::string> &SavedStrings) {
348   for (int i = 0; i < argc; ++i) {
349     const char *Arg = argv[i];
350     if (Arg[0] != '@') {
351       ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg)));
352       continue;
353     }
354 
355     ExpandArgsFromBuf(Arg, ArgVector, SavedStrings);
356   }
357 }
358