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