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