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