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