• 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/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__anon19b69e5b0111::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,llvm::sys::fs::OpenFlags Flags,std::string * Error,clang::DiagnosticsEngine * DiagEngine)119 OpenOutputFile(const char *OutputFile,
120                llvm::sys::fs::OpenFlags 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.LaxVectorConversions = 0;  // Do not bitcast vectors!
161     LangOpts.CharIsSigned = 1;  // Signed char is our default.
162 
163     CodeGenOpts.OptimizationLevel = 3;
164 
165     GlobalInitialized = true;
166   }
167 }
168 
LLVMErrorHandler(void * UserData,const std::string & Message,bool GenCrashDialog)169 void Slang::LLVMErrorHandler(void *UserData, const std::string &Message,
170                              bool GenCrashDialog) {
171   clang::DiagnosticsEngine* DiagEngine =
172     static_cast<clang::DiagnosticsEngine *>(UserData);
173 
174   DiagEngine->Report(clang::diag::err_fe_error_backend) << Message;
175   exit(1);
176 }
177 
createTarget(const std::string & Triple,const std::string & CPU,const std::vector<std::string> & Features)178 void Slang::createTarget(const std::string &Triple, const std::string &CPU,
179                          const std::vector<std::string> &Features) {
180   if (!Triple.empty())
181     mTargetOpts->Triple = Triple;
182   else
183     mTargetOpts->Triple = DEFAULT_TARGET_TRIPLE_STRING;
184 
185   if (!CPU.empty())
186     mTargetOpts->CPU = CPU;
187 
188   if (!Features.empty())
189     mTargetOpts->FeaturesAsWritten = Features;
190 
191   mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine,
192                                                     mTargetOpts.getPtr()));
193 }
194 
createFileManager()195 void Slang::createFileManager() {
196   mFileSysOpt.reset(new clang::FileSystemOptions());
197   mFileMgr.reset(new clang::FileManager(*mFileSysOpt));
198 }
199 
createSourceManager()200 void Slang::createSourceManager() {
201   mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr));
202 }
203 
createPreprocessor()204 void Slang::createPreprocessor() {
205   // Default only search header file in current dir
206   llvm::IntrusiveRefCntPtr<clang::HeaderSearchOptions> HSOpts =
207       new clang::HeaderSearchOptions();
208   clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(HSOpts,
209                                                             *mFileMgr,
210                                                             *mDiagEngine,
211                                                             LangOpts,
212                                                             mTarget.get());
213 
214   llvm::IntrusiveRefCntPtr<clang::PreprocessorOptions> PPOpts =
215       new clang::PreprocessorOptions();
216   mPP.reset(new clang::Preprocessor(PPOpts,
217                                     *mDiagEngine,
218                                     LangOpts,
219                                     mTarget.get(),
220                                     *mSourceMgr,
221                                     *HeaderInfo,
222                                     *this,
223                                     NULL,
224                                     /* OwnsHeaderSearch = */true));
225   // Initialize the preprocessor
226   mPragmas.clear();
227   mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas));
228 
229   std::vector<clang::DirectoryLookup> SearchList;
230   for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) {
231     if (const clang::DirectoryEntry *DE =
232             mFileMgr->getDirectory(mIncludePaths[i])) {
233       SearchList.push_back(clang::DirectoryLookup(DE,
234                                                   clang::SrcMgr::C_System,
235                                                   false));
236     }
237   }
238 
239   HeaderInfo->SetSearchPaths(SearchList,
240                              /* angledDirIdx = */1,
241                              /* systemDixIdx = */1,
242                              /* noCurDirSearch = */false);
243 
244   initPreprocessor();
245 }
246 
createASTContext()247 void Slang::createASTContext() {
248   mASTContext.reset(new clang::ASTContext(LangOpts,
249                                           *mSourceMgr,
250                                           mTarget.get(),
251                                           mPP->getIdentifierTable(),
252                                           mPP->getSelectorTable(),
253                                           mPP->getBuiltinInfo(),
254                                           /* size_reserve = */0));
255   initASTContext();
256 }
257 
258 clang::ASTConsumer *
createBackend(const clang::CodeGenOptions & CodeGenOpts,llvm::raw_ostream * OS,OutputType OT)259 Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts,
260                      llvm::raw_ostream *OS, OutputType OT) {
261   return new Backend(mDiagEngine, CodeGenOpts, getTargetOptions(),
262                      &mPragmas, OS, OT);
263 }
264 
Slang()265 Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) {
266   mTargetOpts = new clang::TargetOptions();
267   GlobalInitialization();
268 }
269 
init(const std::string & Triple,const std::string & CPU,const std::vector<std::string> & Features,clang::DiagnosticsEngine * DiagEngine,DiagnosticBuffer * DiagClient)270 void Slang::init(const std::string &Triple, const std::string &CPU,
271                  const std::vector<std::string> &Features,
272                  clang::DiagnosticsEngine *DiagEngine,
273                  DiagnosticBuffer *DiagClient) {
274   if (mInitialized)
275     return;
276 
277   mDiagEngine = DiagEngine;
278   mDiagClient = DiagClient;
279   mDiag.reset(new clang::Diagnostic(mDiagEngine));
280   initDiagnostic();
281   llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine);
282 
283   createTarget(Triple, CPU, Features);
284   createFileManager();
285   createSourceManager();
286 
287   mInitialized = true;
288 }
289 
loadModule(clang::SourceLocation ImportLoc,clang::ModuleIdPath Path,clang::Module::NameVisibilityKind Visibility,bool IsInclusionDirective)290 clang::ModuleLoadResult Slang::loadModule(
291     clang::SourceLocation ImportLoc,
292     clang::ModuleIdPath Path,
293     clang::Module::NameVisibilityKind Visibility,
294     bool IsInclusionDirective) {
295   slangAssert(0 && "Not implemented");
296   return clang::ModuleLoadResult();
297 }
298 
setInputSource(llvm::StringRef InputFile,const char * Text,size_t TextLength)299 bool Slang::setInputSource(llvm::StringRef InputFile,
300                            const char *Text,
301                            size_t TextLength) {
302   mInputFileName = InputFile.str();
303 
304   // Reset the ID tables if we are reusing the SourceManager
305   mSourceMgr->clearIDTables();
306 
307   // Load the source
308   llvm::MemoryBuffer *SB =
309       llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength);
310   mSourceMgr->createMainFileIDForMemBuffer(SB);
311 
312   if (mSourceMgr->getMainFileID().isInvalid()) {
313     mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
314     return false;
315   }
316   return true;
317 }
318 
setInputSource(llvm::StringRef InputFile)319 bool Slang::setInputSource(llvm::StringRef InputFile) {
320   mInputFileName = InputFile.str();
321 
322   mSourceMgr->clearIDTables();
323 
324   const clang::FileEntry *File = mFileMgr->getFile(InputFile);
325   if (File)
326     mSourceMgr->createMainFileID(File);
327 
328   if (mSourceMgr->getMainFileID().isInvalid()) {
329     mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
330     return false;
331   }
332 
333   return true;
334 }
335 
setOutput(const char * OutputFile)336 bool Slang::setOutput(const char *OutputFile) {
337   std::string Error;
338   llvm::tool_output_file *OS = NULL;
339 
340   switch (mOT) {
341     case OT_Dependency:
342     case OT_Assembly:
343     case OT_LLVMAssembly: {
344       OS = OpenOutputFile(OutputFile, llvm::sys::fs::F_None, &Error,
345           mDiagEngine);
346       break;
347     }
348     case OT_Nothing: {
349       break;
350     }
351     case OT_Object:
352     case OT_Bitcode: {
353       OS = OpenOutputFile(OutputFile, llvm::sys::fs::F_Binary,
354                           &Error, mDiagEngine);
355       break;
356     }
357     default: {
358       llvm_unreachable("Unknown compiler output type");
359     }
360   }
361 
362   if (!Error.empty())
363     return false;
364 
365   mOS.reset(OS);
366 
367   mOutputFileName = OutputFile;
368 
369   return true;
370 }
371 
setDepOutput(const char * OutputFile)372 bool Slang::setDepOutput(const char *OutputFile) {
373   std::string Error;
374 
375   mDOS.reset(
376       OpenOutputFile(OutputFile, llvm::sys::fs::F_None, &Error, mDiagEngine));
377   if (!Error.empty() || (mDOS.get() == NULL))
378     return false;
379 
380   mDepOutputFileName = OutputFile;
381 
382   return true;
383 }
384 
generateDepFile()385 int Slang::generateDepFile() {
386   if (mDiagEngine->hasErrorOccurred())
387     return 1;
388   if (mDOS.get() == NULL)
389     return 1;
390 
391   // Initialize options for generating dependency file
392   clang::DependencyOutputOptions DepOpts;
393   DepOpts.IncludeSystemHeaders = 1;
394   DepOpts.OutputFile = mDepOutputFileName;
395   DepOpts.Targets = mAdditionalDepTargets;
396   DepOpts.Targets.push_back(mDepTargetBCFileName);
397   for (std::vector<std::string>::const_iterator
398            I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end();
399        I != E;
400        I++) {
401     DepOpts.Targets.push_back(*I);
402   }
403   mGeneratedFileNames.clear();
404 
405   // Per-compilation needed initialization
406   createPreprocessor();
407   AttachDependencyFileGen(*mPP.get(), DepOpts);
408 
409   // Inform the diagnostic client we are processing a source file
410   mDiagClient->BeginSourceFile(LangOpts, mPP.get());
411 
412   // Go through the source file (no operations necessary)
413   clang::Token Tok;
414   mPP->EnterMainSourceFile();
415   do {
416     mPP->Lex(Tok);
417   } while (Tok.isNot(clang::tok::eof));
418 
419   mPP->EndSourceFile();
420 
421   // Declare success if no error
422   if (!mDiagEngine->hasErrorOccurred())
423     mDOS->keep();
424 
425   // Clean up after compilation
426   mPP.reset();
427   mDOS.reset();
428 
429   return mDiagEngine->hasErrorOccurred() ? 1 : 0;
430 }
431 
compile()432 int Slang::compile() {
433   if (mDiagEngine->hasErrorOccurred())
434     return 1;
435   if (mOS.get() == NULL)
436     return 1;
437 
438   // Here is per-compilation needed initialization
439   createPreprocessor();
440   createASTContext();
441 
442   mBackend.reset(createBackend(CodeGenOpts, &mOS->os(), mOT));
443 
444   // Inform the diagnostic client we are processing a source file
445   mDiagClient->BeginSourceFile(LangOpts, mPP.get());
446 
447   // The core of the slang compiler
448   ParseAST(*mPP, mBackend.get(), *mASTContext);
449 
450   // Inform the diagnostic client we are done with previous source file
451   mDiagClient->EndSourceFile();
452 
453   // Declare success if no error
454   if (!mDiagEngine->hasErrorOccurred())
455     mOS->keep();
456 
457   // The compilation ended, clear
458   mBackend.reset();
459   mASTContext.reset();
460   mPP.reset();
461   mOS.reset();
462 
463   return mDiagEngine->hasErrorOccurred() ? 1 : 0;
464 }
465 
setDebugMetadataEmission(bool EmitDebug)466 void Slang::setDebugMetadataEmission(bool EmitDebug) {
467   if (EmitDebug)
468     CodeGenOpts.setDebugInfo(clang::CodeGenOptions::FullDebugInfo);
469   else
470     CodeGenOpts.setDebugInfo(clang::CodeGenOptions::NoDebugInfo);
471 }
472 
setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel)473 void Slang::setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel) {
474   CodeGenOpts.OptimizationLevel = OptimizationLevel;
475 }
476 
reset()477 void Slang::reset() {
478   llvm::errs() << mDiagClient->str();
479   mDiagEngine->Reset();
480   mDiagClient->reset();
481 }
482 
~Slang()483 Slang::~Slang() {
484   llvm::llvm_shutdown();
485 }
486 
487 }  // namespace slang
488