slang.cpp revision b81c6a4cbd9c08e0b20ea4fbc615b416ac1bc9ec
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
109void Slang::GlobalInitialization() {
110  if (!GlobalInitialized) {
111    // We only support x86, x64 and ARM target
112
113    // For ARM
114    LLVMInitializeARMTargetInfo();
115    LLVMInitializeARMTarget();
116    LLVMInitializeARMAsmPrinter();
117
118    // For x86 and x64
119    LLVMInitializeX86TargetInfo();
120    LLVMInitializeX86Target();
121    LLVMInitializeX86AsmPrinter();
122
123    // Please refer to include/clang/Basic/LangOptions.h to setup
124    // the options.
125    LangOpts.RTTI = 0;  // Turn off the RTTI information support
126    LangOpts.NeXTRuntime = 0;   // Turn off the NeXT runtime uses
127    LangOpts.Bool = 1;  // Turn on 'bool', 'true', 'false' keywords
128
129    CodeGenOpts.OptimizationLevel = 3;  /* -O3 */
130
131    GlobalInitialized = true;
132  }
133
134  return;
135}
136
137void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) {
138  clang::Diagnostic* Diags = static_cast<clang::Diagnostic*>(UserData);
139  Diags->Report(clang::diag::err_fe_error_backend) << Message;
140  exit(1);
141}
142
143void Slang::createDiagnostic() {
144  mDiagnostics =
145      llvm::IntrusiveRefCntPtr<clang::Diagnostic>(new clang::Diagnostic());
146  mDiagClient = new DiagnosticBuffer();
147  // This takes the ownership of mDiagClient.
148  mDiagnostics->setClient(mDiagClient);
149  return;
150}
151
152void Slang::createTarget(const std::string &Triple, const std::string &CPU,
153                         const std::vector<std::string> &Features) {
154  if (!Triple.empty())
155    mTargetOpts.Triple = Triple;
156  else
157    mTargetOpts.Triple = DEFAULT_TARGET_TRIPLE_STRING;
158
159  if (!CPU.empty())
160    mTargetOpts.CPU = CPU;
161
162  if (!Features.empty())
163    mTargetOpts.Features = Features;
164
165  mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagnostics,
166                                                    mTargetOpts));
167
168  return;
169}
170
171void Slang::createFileManager() {
172  mFileMgr.reset(new clang::FileManager());
173}
174
175void Slang::createSourceManager() {
176  mSourceMgr.reset(new clang::SourceManager(*mDiagnostics));
177  return;
178}
179
180void Slang::createPreprocessor() {
181  // Default only search header file in current dir
182  clang::HeaderSearch *HS = new clang::HeaderSearch(*mFileMgr);
183
184  mPP.reset(new clang::Preprocessor(*mDiagnostics,
185                                    LangOpts,
186                                    *mTarget,
187                                    *mSourceMgr,
188                                    *HS,
189                                    NULL,
190                                    /* OwnsHeaderSearch = */true));
191  // Initialize the prepocessor
192  mPragmas.clear();
193  mPP->AddPragmaHandler(new PragmaRecorder(mPragmas));
194
195  std::vector<clang::DirectoryLookup> SearchList;
196  for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) {
197    if (const clang::DirectoryEntry *DE =
198            mFileMgr->getDirectory(mIncludePaths[i])) {
199      SearchList.push_back(clang::DirectoryLookup(DE,
200                                                  clang::SrcMgr::C_System,
201                                                  false,
202                                                  false));
203    }
204  }
205
206  HS->SetSearchPaths(SearchList, 1, false);
207
208  initPreprocessor();
209  return;
210}
211
212void Slang::createASTContext() {
213  mASTContext.reset(new clang::ASTContext(LangOpts,
214                                          *mSourceMgr,
215                                          *mTarget,
216                                          mPP->getIdentifierTable(),
217                                          mPP->getSelectorTable(),
218                                          mPP->getBuiltinInfo(),
219                                          /* size_reserve = */0));
220  initASTContext();
221  return;
222}
223
224clang::ASTConsumer
225*Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts,
226                      llvm::raw_ostream *OS,
227                      OutputType OT) {
228  return new Backend(*mDiagnostics,
229                     CodeGenOpts,
230                     mTargetOpts,
231                     mPragmas,
232                     OS,
233                     OT);
234}
235
236Slang::Slang(const std::string &Triple, const std::string &CPU,
237             const std::vector<std::string> &Features)
238    : mDiagClient(NULL),
239      mOT(OT_Default) {
240  GlobalInitialization();
241
242  createDiagnostic();
243  llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagnostics.getPtr());
244
245  createTarget(Triple, CPU, Features);
246  createFileManager();
247  createSourceManager();
248
249  return;
250}
251
252bool Slang::setInputSource(llvm::StringRef InputFile,
253                           const char *Text,
254                           size_t TextLength) {
255  mInputFileName = InputFile.str();
256
257  // Reset the ID tables if we are reusing the SourceManager
258  mSourceMgr->clearIDTables();
259
260  // Load the source
261  llvm::MemoryBuffer *SB =
262      llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength);
263  mSourceMgr->createMainFileIDForMemBuffer(SB);
264
265  if (mSourceMgr->getMainFileID().isInvalid()) {
266    mDiagnostics->Report(clang::diag::err_fe_error_reading) << InputFile;
267    return false;
268  }
269  return true;
270}
271
272bool Slang::setInputSource(llvm::StringRef InputFile) {
273  mInputFileName = InputFile.str();
274
275  mSourceMgr->clearIDTables();
276
277  const clang::FileEntry *File = mFileMgr->getFile(InputFile);
278  if (File)
279    mSourceMgr->createMainFileID(File);
280
281  if (mSourceMgr->getMainFileID().isInvalid()) {
282    mDiagnostics->Report(clang::diag::err_fe_error_reading) << InputFile;
283    return false;
284  }
285
286  return true;
287}
288
289bool Slang::setOutput(const char *OutputFile) {
290  llvm::sys::Path OutputFilePath(OutputFile);
291  std::string Error;
292
293  switch (mOT) {
294    case OT_Dependency:
295    case OT_Assembly:
296    case OT_LLVMAssembly: {
297      if (!SlangUtils::CreateDirectoryWithParents(OutputFilePath.getDirname(),
298                                                  &Error))
299        mDiagnostics->Report(clang::diag::err_fe_error_opening) << OutputFile
300            << Error;
301      mOS.reset(new llvm::tool_output_file(OutputFile, Error, 0));
302      break;
303    }
304    case OT_Nothing: {
305      mOS.reset();
306      break;
307    }
308    case OT_Object:
309    case OT_Bitcode: {
310      if (!SlangUtils::CreateDirectoryWithParents(OutputFilePath.getDirname(),
311                                                  &Error))
312        mDiagnostics->Report(clang::diag::err_fe_error_opening) << OutputFile
313            << Error;
314      mOS.reset(new llvm::tool_output_file(OutputFile,
315                                           Error,
316                                           llvm::raw_fd_ostream::F_Binary));
317      break;
318    }
319    default: {
320      llvm_unreachable("Unknown compiler output type");
321    }
322  }
323
324  if (!Error.empty()) {
325    mOS.reset();
326    mDiagnostics->Report(clang::diag::err_fe_error_opening) << OutputFile
327                                                            << Error;
328    return false;
329  }
330
331  mOutputFileName = OutputFile;
332
333  return true;
334}
335
336bool Slang::setDepOutput(const char *OutputFile) {
337  llvm::sys::Path OutputFilePath(OutputFile);
338  std::string Error;
339
340  if (!SlangUtils::CreateDirectoryWithParents(OutputFilePath.getDirname(),
341                                              &Error))
342    mDiagnostics->Report(clang::diag::err_fe_error_opening) << OutputFile
343        << Error;
344  mDOS.reset(new llvm::raw_fd_ostream(OutputFile, Error, 0));
345
346  if (!Error.empty()) {
347    mDOS.reset();
348    mDiagnostics->Report(clang::diag::err_fe_error_opening) << OutputFile
349                                                            << Error;
350    return false;
351  }
352
353  mDepOutputFileName = OutputFile;
354
355  return true;
356}
357
358int Slang::generateDepFile() {
359  if (mDiagnostics->getNumErrors() > 0)
360    return mDiagnostics->getNumErrors();
361  if (mDOS.get() == NULL)
362    return 1;
363
364  /* Initialize options for generating dependency file */
365  clang::DependencyOutputOptions DepOpts;
366  DepOpts.IncludeSystemHeaders = 1;
367  DepOpts.OutputFile = mDepOutputFileName;
368  DepOpts.Targets = mAdditionalDepTargets;
369  DepOpts.Targets.push_back(mDepTargetBCFileName);
370
371  /* Per-compilation needed initialization */
372  createPreprocessor();
373  AttachDependencyFileGen(*mPP.get(), DepOpts);
374
375  /* Inform the diagnostic client we are processing a source file */
376  mDiagClient->BeginSourceFile(LangOpts, mPP.get());
377
378  /* Go through the source file (no operations necessary) */
379  clang::Token Tok;
380  mPP->EnterMainSourceFile();
381  do {
382    mPP->Lex(Tok);
383  } while (Tok.isNot(clang::tok::eof));
384
385  mPP->EndSourceFile();
386
387  /* Clean up after compilation */
388  mPP.reset();
389
390  return mDiagnostics->getNumErrors();
391}
392
393int Slang::compile() {
394  if (mDiagnostics->getNumErrors() > 0)
395    return mDiagnostics->getNumErrors();
396  if (mOS.get() == NULL)
397    return 1;
398
399  // Here is per-compilation needed initialization
400  createPreprocessor();
401  createASTContext();
402
403  mBackend.reset(createBackend(CodeGenOpts, mOS.take(), mOT));
404
405  // Inform the diagnostic client we are processing a source file
406  mDiagClient->BeginSourceFile(LangOpts, mPP.get());
407
408  // The core of the slang compiler
409  ParseAST(*mPP, mBackend.get(), *mASTContext);
410
411  // The compilation ended, clear
412  mBackend.reset();
413  mASTContext.reset();
414  mPP.reset();
415
416  // Inform the diagnostic client we are done with previous source file
417  mDiagClient->EndSourceFile();
418
419  return mDiagnostics->getNumErrors();
420}
421
422void Slang::reset() {
423  mDiagnostics->Reset();
424  mDiagClient->reset();
425  return;
426}
427
428Slang::~Slang() {
429  llvm::llvm_shutdown();
430  return;
431}
432