1 /*
2 * Copyright 2010, 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 "slang.h"
18
19 #include <stdlib.h>
20
21 #include <string>
22 #include <vector>
23
24 #include "clang/AST/ASTConsumer.h"
25 #include "clang/AST/ASTContext.h"
26
27 #include "clang/Basic/DiagnosticIDs.h"
28 #include "clang/Basic/DiagnosticOptions.h"
29 #include "clang/Basic/FileManager.h"
30 #include "clang/Basic/FileSystemOptions.h"
31 #include "clang/Basic/LangOptions.h"
32 #include "clang/Basic/SourceManager.h"
33 #include "clang/Basic/TargetInfo.h"
34 #include "clang/Basic/TargetOptions.h"
35
36 #include "clang/Frontend/CodeGenOptions.h"
37 #include "clang/Frontend/DependencyOutputOptions.h"
38 #include "clang/Frontend/FrontendDiagnostic.h"
39 #include "clang/Frontend/TextDiagnosticPrinter.h"
40 #include "clang/Frontend/Utils.h"
41
42 #include "clang/Lex/Preprocessor.h"
43 #include "clang/Lex/PreprocessorOptions.h"
44 #include "clang/Lex/HeaderSearch.h"
45 #include "clang/Lex/HeaderSearchOptions.h"
46
47 #include "clang/Parse/ParseAST.h"
48
49 #include "llvm/ADT/IntrusiveRefCntPtr.h"
50
51 #include "llvm/Bitcode/ReaderWriter.h"
52
53 // More force linking
54 #include "llvm/Linker.h"
55
56 // Force linking all passes/vmcore stuffs to libslang.so
57 #include "llvm/LinkAllIR.h"
58 #include "llvm/LinkAllPasses.h"
59
60 #include "llvm/Support/raw_ostream.h"
61 #include "llvm/Support/MemoryBuffer.h"
62 #include "llvm/Support/ErrorHandling.h"
63 #include "llvm/Support/ManagedStatic.h"
64 #include "llvm/Support/Path.h"
65 #include "llvm/Support/TargetSelect.h"
66 #include "llvm/Support/ToolOutputFile.h"
67
68 #include "slang_assert.h"
69 #include "slang_backend.h"
70 #include "slang_utils.h"
71
72 namespace {
73
74 struct ForceSlangLinking {
ForceSlangLinking__anon8be243f70111::ForceSlangLinking75 ForceSlangLinking() {
76 // We must reference the functions in such a way that compilers will not
77 // delete it all as dead code, even with whole program optimization,
78 // yet is effectively a NO-OP. As the compiler isn't smart enough
79 // to know that getenv() never returns -1, this will do the job.
80 if (std::getenv("bar") != reinterpret_cast<char*>(-1))
81 return;
82
83 // llvm-rs-link needs following functions existing in libslang.
84 llvm::ParseBitcodeFile(NULL, llvm::getGlobalContext(), NULL);
85 llvm::Linker::LinkModules(NULL, NULL, 0, NULL);
86
87 // llvm-rs-cc need this.
88 new clang::TextDiagnosticPrinter(llvm::errs(),
89 new clang::DiagnosticOptions());
90 }
91 } ForceSlangLinking;
92
93 } // namespace
94
95 namespace slang {
96
97 #if defined(__arm__)
98 # define DEFAULT_TARGET_TRIPLE_STRING "armv7-none-linux-gnueabi"
99 #elif defined(__x86_64__)
100 # define DEFAULT_TARGET_TRIPLE_STRING "x86_64-unknown-linux"
101 #else
102 // let's use x86 as default target
103 # define DEFAULT_TARGET_TRIPLE_STRING "i686-unknown-linux"
104 #endif
105
106 bool Slang::GlobalInitialized = false;
107
108 // Language option (define the language feature for compiler such as C99)
109 clang::LangOptions Slang::LangOpts;
110
111 // Code generation option for the compiler
112 clang::CodeGenOptions Slang::CodeGenOpts;
113
114 // The named of metadata node that pragma resides (should be synced with
115 // bcc.cpp)
116 const llvm::StringRef Slang::PragmaMetadataName = "#pragma";
117
118 static inline llvm::tool_output_file *
OpenOutputFile(const char * OutputFile,unsigned Flags,std::string * Error,clang::DiagnosticsEngine * DiagEngine)119 OpenOutputFile(const char *OutputFile,
120 unsigned Flags,
121 std::string* Error,
122 clang::DiagnosticsEngine *DiagEngine) {
123 slangAssert((OutputFile != NULL) && (Error != NULL) &&
124 (DiagEngine != NULL) && "Invalid parameter!");
125
126 if (SlangUtils::CreateDirectoryWithParents(
127 llvm::sys::path::parent_path(OutputFile), Error)) {
128 llvm::tool_output_file *F =
129 new llvm::tool_output_file(OutputFile, *Error, Flags);
130 if (F != NULL)
131 return F;
132 }
133
134 // Report error here.
135 DiagEngine->Report(clang::diag::err_fe_error_opening)
136 << OutputFile << *Error;
137
138 return NULL;
139 }
140
GlobalInitialization()141 void Slang::GlobalInitialization() {
142 if (!GlobalInitialized) {
143 // We only support x86, x64 and ARM target
144
145 // For ARM
146 LLVMInitializeARMTargetInfo();
147 LLVMInitializeARMTarget();
148 LLVMInitializeARMAsmPrinter();
149
150 // For x86 and x64
151 LLVMInitializeX86TargetInfo();
152 LLVMInitializeX86Target();
153 LLVMInitializeX86AsmPrinter();
154
155 // Please refer to include/clang/Basic/LangOptions.h to setup
156 // the options.
157 LangOpts.RTTI = 0; // Turn off the RTTI information support
158 LangOpts.C99 = 1;
159 LangOpts.Renderscript = 1;
160 LangOpts.CharIsSigned = 1; // Signed char is our default.
161
162 CodeGenOpts.OptimizationLevel = 3;
163
164 GlobalInitialized = true;
165 }
166 }
167
LLVMErrorHandler(void * UserData,const std::string & Message)168 void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) {
169 clang::DiagnosticsEngine* DiagEngine =
170 static_cast<clang::DiagnosticsEngine *>(UserData);
171
172 DiagEngine->Report(clang::diag::err_fe_error_backend) << Message;
173 exit(1);
174 }
175
createTarget(const std::string & Triple,const std::string & CPU,const std::vector<std::string> & Features)176 void Slang::createTarget(const std::string &Triple, const std::string &CPU,
177 const std::vector<std::string> &Features) {
178 if (!Triple.empty())
179 mTargetOpts->Triple = Triple;
180 else
181 mTargetOpts->Triple = DEFAULT_TARGET_TRIPLE_STRING;
182
183 if (!CPU.empty())
184 mTargetOpts->CPU = CPU;
185
186 if (!Features.empty())
187 mTargetOpts->FeaturesAsWritten = Features;
188
189 mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine,
190 mTargetOpts.getPtr()));
191 }
192
createFileManager()193 void Slang::createFileManager() {
194 mFileSysOpt.reset(new clang::FileSystemOptions());
195 mFileMgr.reset(new clang::FileManager(*mFileSysOpt));
196 }
197
createSourceManager()198 void Slang::createSourceManager() {
199 mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr));
200 }
201
createPreprocessor()202 void Slang::createPreprocessor() {
203 // Default only search header file in current dir
204 llvm::IntrusiveRefCntPtr<clang::HeaderSearchOptions> HSOpts =
205 new clang::HeaderSearchOptions();
206 clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(HSOpts,
207 *mFileMgr,
208 *mDiagEngine,
209 LangOpts,
210 mTarget.get());
211
212 llvm::IntrusiveRefCntPtr<clang::PreprocessorOptions> PPOpts =
213 new clang::PreprocessorOptions();
214 mPP.reset(new clang::Preprocessor(PPOpts,
215 *mDiagEngine,
216 LangOpts,
217 mTarget.get(),
218 *mSourceMgr,
219 *HeaderInfo,
220 *this,
221 NULL,
222 /* OwnsHeaderSearch = */true));
223 // Initialize the preprocessor
224 mPragmas.clear();
225 mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas));
226
227 std::vector<clang::DirectoryLookup> SearchList;
228 for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) {
229 if (const clang::DirectoryEntry *DE =
230 mFileMgr->getDirectory(mIncludePaths[i])) {
231 SearchList.push_back(clang::DirectoryLookup(DE,
232 clang::SrcMgr::C_System,
233 false));
234 }
235 }
236
237 HeaderInfo->SetSearchPaths(SearchList,
238 /* angledDirIdx = */1,
239 /* systemDixIdx = */1,
240 /* noCurDirSearch = */false);
241
242 initPreprocessor();
243 }
244
createASTContext()245 void Slang::createASTContext() {
246 mASTContext.reset(new clang::ASTContext(LangOpts,
247 *mSourceMgr,
248 mTarget.get(),
249 mPP->getIdentifierTable(),
250 mPP->getSelectorTable(),
251 mPP->getBuiltinInfo(),
252 /* size_reserve = */0));
253 initASTContext();
254 }
255
256 clang::ASTConsumer *
createBackend(const clang::CodeGenOptions & CodeGenOpts,llvm::raw_ostream * OS,OutputType OT)257 Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts,
258 llvm::raw_ostream *OS, OutputType OT) {
259 return new Backend(mDiagEngine, CodeGenOpts, getTargetOptions(),
260 &mPragmas, OS, OT);
261 }
262
Slang()263 Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) {
264 mTargetOpts = new clang::TargetOptions();
265 GlobalInitialization();
266 }
267
init(const std::string & Triple,const std::string & CPU,const std::vector<std::string> & Features,clang::DiagnosticsEngine * DiagEngine,DiagnosticBuffer * DiagClient)268 void Slang::init(const std::string &Triple, const std::string &CPU,
269 const std::vector<std::string> &Features,
270 clang::DiagnosticsEngine *DiagEngine,
271 DiagnosticBuffer *DiagClient) {
272 if (mInitialized)
273 return;
274
275 mDiagEngine = DiagEngine;
276 mDiagClient = DiagClient;
277 mDiag.reset(new clang::Diagnostic(mDiagEngine));
278 initDiagnostic();
279 llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine);
280
281 createTarget(Triple, CPU, Features);
282 createFileManager();
283 createSourceManager();
284
285 mInitialized = true;
286 }
287
loadModule(clang::SourceLocation ImportLoc,clang::ModuleIdPath Path,clang::Module::NameVisibilityKind Visibility,bool IsInclusionDirective)288 clang::ModuleLoadResult Slang::loadModule(
289 clang::SourceLocation ImportLoc,
290 clang::ModuleIdPath Path,
291 clang::Module::NameVisibilityKind Visibility,
292 bool IsInclusionDirective) {
293 slangAssert(0 && "Not implemented");
294 return clang::ModuleLoadResult();
295 }
296
setInputSource(llvm::StringRef InputFile,const char * Text,size_t TextLength)297 bool Slang::setInputSource(llvm::StringRef InputFile,
298 const char *Text,
299 size_t TextLength) {
300 mInputFileName = InputFile.str();
301
302 // Reset the ID tables if we are reusing the SourceManager
303 mSourceMgr->clearIDTables();
304
305 // Load the source
306 llvm::MemoryBuffer *SB =
307 llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength);
308 mSourceMgr->createMainFileIDForMemBuffer(SB);
309
310 if (mSourceMgr->getMainFileID().isInvalid()) {
311 mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
312 return false;
313 }
314 return true;
315 }
316
setInputSource(llvm::StringRef InputFile)317 bool Slang::setInputSource(llvm::StringRef InputFile) {
318 mInputFileName = InputFile.str();
319
320 mSourceMgr->clearIDTables();
321
322 const clang::FileEntry *File = mFileMgr->getFile(InputFile);
323 if (File)
324 mSourceMgr->createMainFileID(File);
325
326 if (mSourceMgr->getMainFileID().isInvalid()) {
327 mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
328 return false;
329 }
330
331 return true;
332 }
333
setOutput(const char * OutputFile)334 bool Slang::setOutput(const char *OutputFile) {
335 llvm::sys::Path OutputFilePath(OutputFile);
336 std::string Error;
337 llvm::tool_output_file *OS = NULL;
338
339 switch (mOT) {
340 case OT_Dependency:
341 case OT_Assembly:
342 case OT_LLVMAssembly: {
343 OS = OpenOutputFile(OutputFile, 0, &Error, mDiagEngine);
344 break;
345 }
346 case OT_Nothing: {
347 break;
348 }
349 case OT_Object:
350 case OT_Bitcode: {
351 OS = OpenOutputFile(OutputFile, llvm::raw_fd_ostream::F_Binary,
352 &Error, mDiagEngine);
353 break;
354 }
355 default: {
356 llvm_unreachable("Unknown compiler output type");
357 }
358 }
359
360 if (!Error.empty())
361 return false;
362
363 mOS.reset(OS);
364
365 mOutputFileName = OutputFile;
366
367 return true;
368 }
369
setDepOutput(const char * OutputFile)370 bool Slang::setDepOutput(const char *OutputFile) {
371 llvm::sys::Path OutputFilePath(OutputFile);
372 std::string Error;
373
374 mDOS.reset(OpenOutputFile(OutputFile, 0, &Error, mDiagEngine));
375 if (!Error.empty() || (mDOS.get() == NULL))
376 return false;
377
378 mDepOutputFileName = OutputFile;
379
380 return true;
381 }
382
generateDepFile()383 int Slang::generateDepFile() {
384 if (mDiagEngine->hasErrorOccurred())
385 return 1;
386 if (mDOS.get() == NULL)
387 return 1;
388
389 // Initialize options for generating dependency file
390 clang::DependencyOutputOptions DepOpts;
391 DepOpts.IncludeSystemHeaders = 1;
392 DepOpts.OutputFile = mDepOutputFileName;
393 DepOpts.Targets = mAdditionalDepTargets;
394 DepOpts.Targets.push_back(mDepTargetBCFileName);
395 for (std::vector<std::string>::const_iterator
396 I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end();
397 I != E;
398 I++) {
399 DepOpts.Targets.push_back(*I);
400 }
401 mGeneratedFileNames.clear();
402
403 // Per-compilation needed initialization
404 createPreprocessor();
405 AttachDependencyFileGen(*mPP.get(), DepOpts);
406
407 // Inform the diagnostic client we are processing a source file
408 mDiagClient->BeginSourceFile(LangOpts, mPP.get());
409
410 // Go through the source file (no operations necessary)
411 clang::Token Tok;
412 mPP->EnterMainSourceFile();
413 do {
414 mPP->Lex(Tok);
415 } while (Tok.isNot(clang::tok::eof));
416
417 mPP->EndSourceFile();
418
419 // Declare success if no error
420 if (!mDiagEngine->hasErrorOccurred())
421 mDOS->keep();
422
423 // Clean up after compilation
424 mPP.reset();
425 mDOS.reset();
426
427 return mDiagEngine->hasErrorOccurred() ? 1 : 0;
428 }
429
compile()430 int Slang::compile() {
431 if (mDiagEngine->hasErrorOccurred())
432 return 1;
433 if (mOS.get() == NULL)
434 return 1;
435
436 // Here is per-compilation needed initialization
437 createPreprocessor();
438 createASTContext();
439
440 mBackend.reset(createBackend(CodeGenOpts, &mOS->os(), mOT));
441
442 // Inform the diagnostic client we are processing a source file
443 mDiagClient->BeginSourceFile(LangOpts, mPP.get());
444
445 // The core of the slang compiler
446 ParseAST(*mPP, mBackend.get(), *mASTContext);
447
448 // Inform the diagnostic client we are done with previous source file
449 mDiagClient->EndSourceFile();
450
451 // Declare success if no error
452 if (!mDiagEngine->hasErrorOccurred())
453 mOS->keep();
454
455 // The compilation ended, clear
456 mBackend.reset();
457 mASTContext.reset();
458 mPP.reset();
459 mOS.reset();
460
461 return mDiagEngine->hasErrorOccurred() ? 1 : 0;
462 }
463
setDebugMetadataEmission(bool EmitDebug)464 void Slang::setDebugMetadataEmission(bool EmitDebug) {
465 if (EmitDebug)
466 CodeGenOpts.setDebugInfo(clang::CodeGenOptions::FullDebugInfo);
467 else
468 CodeGenOpts.setDebugInfo(clang::CodeGenOptions::NoDebugInfo);
469 }
470
setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel)471 void Slang::setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel) {
472 CodeGenOpts.OptimizationLevel = OptimizationLevel;
473 }
474
reset()475 void Slang::reset() {
476 llvm::errs() << mDiagClient->str();
477 mDiagEngine->Reset();
478 mDiagClient->reset();
479 }
480
~Slang()481 Slang::~Slang() {
482 llvm::llvm_shutdown();
483 }
484
485 } // namespace slang
486