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