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:
save(const char * Str)54 const char *save(const char *Str) {
55 return Strings.save(Str);
56 }
57
StringSet()58 StringSet() : Strings(A), A() {}
59
getStringSaver()60 llvm::StringSaver & getStringSaver() { return Strings; }
61
62 private:
63 llvm::StringSaver Strings;
64 llvm::BumpPtrAllocator A;
65 };
66 }
67
DetermineOutputFile(const std::string & OutputDir,const std::string & PathSuffix,const char * InputFile,slang::Slang::OutputType OutputType,StringSet * SavedStrings)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 */
makeFileList(NamePairList * IOFiles,NamePairList * DepFiles,const llvm::SmallVector<const char *,16> & Inputs,slang::RSCCOptions & Opts,StringSet * SavedStrings)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)
llvm_rs_cc_VersionPrinter()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
LLVMErrorHandler(void * UserData,const std::string & Message,bool GenCrashDialog)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
main(int argc,const char ** argv)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