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