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