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