• 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__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