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