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