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