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