slang.cpp revision 641558f02fe6ce0ee3ae5076eb366c25e2ad5903
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
106const std::string Slang::TargetDescription =
107  "e-"  // little-endian
108  "p:32:32:32-"   // 32-bit pointer
109  "i1:8:8-"
110  "i8:8:8-"
111  "i16:16:16-"
112  "i32:32:32-"
113  "i64:64:64-"
114  "f32:32:32-"
115  "f64:64:64-"
116  "v64:64:64-"  // 64-bit vector (e.g. float2, int2, short4)
117  "v128:128:128-"
118  "a0:0:64-"
119  "n32";  // native CPU only support 32-bit integer width.
120
121// The named of metadata node that pragma resides (should be synced with
122// bcc.cpp)
123const llvm::StringRef Slang::PragmaMetadataName = "#pragma";
124
125static inline llvm::tool_output_file *OpenOutputFile(const char *OutputFile,
126                                                     unsigned Flags,
127                                                     std::string* Error,
128                                                     clang::Diagnostic* Diag) {
129  assert((OutputFile != NULL) && (Error != NULL) && (Diag != NULL) &&
130         "Invalid parameter!");
131
132  llvm::sys::Path OutputFilePath(OutputFile);
133
134  if (SlangUtils::CreateDirectoryWithParents(OutputFilePath.getDirname(),
135                                             Error)) {
136    llvm::tool_output_file *F =
137          new llvm::tool_output_file(OutputFile, *Error, Flags);
138    if (F != NULL)
139      return F;
140  }
141
142  // Report error here.
143  Diag->Report(clang::diag::err_fe_error_opening) << OutputFile << *Error;
144
145  return NULL;
146}
147
148void Slang::GlobalInitialization() {
149  if (!GlobalInitialized) {
150    // We only support x86, x64 and ARM target
151
152    // For ARM
153    LLVMInitializeARMTargetInfo();
154    LLVMInitializeARMTarget();
155    LLVMInitializeARMAsmPrinter();
156
157    // For x86 and x64
158    LLVMInitializeX86TargetInfo();
159    LLVMInitializeX86Target();
160    LLVMInitializeX86AsmPrinter();
161
162    // Please refer to include/clang/Basic/LangOptions.h to setup
163    // the options.
164    LangOpts.RTTI = 0;  // Turn off the RTTI information support
165    LangOpts.NeXTRuntime = 0;   // Turn off the NeXT runtime uses
166    LangOpts.Bool = 1;  // Turn on 'bool', 'true', 'false' keywords
167
168    CodeGenOpts.OptimizationLevel = 3;  /* -O3 */
169
170    GlobalInitialized = true;
171  }
172
173  return;
174}
175
176void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) {
177  clang::Diagnostic* Diags = static_cast<clang::Diagnostic*>(UserData);
178  Diags->Report(clang::diag::err_fe_error_backend) << Message;
179  exit(1);
180}
181
182void Slang::createDiagnostic() {
183  mDiagnostics =
184      llvm::IntrusiveRefCntPtr<clang::Diagnostic>(new clang::Diagnostic());
185  mDiagClient = new DiagnosticBuffer();
186  // This takes the ownership of mDiagClient.
187  mDiagnostics->setClient(mDiagClient);
188  initDiagnostic();
189  return;
190}
191
192void Slang::createTarget(const std::string &Triple, const std::string &CPU,
193                         const std::vector<std::string> &Features) {
194  if (!Triple.empty())
195    mTargetOpts.Triple = Triple;
196  else
197    mTargetOpts.Triple = DEFAULT_TARGET_TRIPLE_STRING;
198
199  if (!CPU.empty())
200    mTargetOpts.CPU = CPU;
201
202  if (!Features.empty())
203    mTargetOpts.Features = Features;
204
205  mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagnostics,
206                                                    mTargetOpts));
207
208  return;
209}
210
211void Slang::createFileManager() {
212  mFileMgr.reset(new clang::FileManager());
213}
214
215void Slang::createSourceManager() {
216  mSourceMgr.reset(new clang::SourceManager(*mDiagnostics));
217  return;
218}
219
220void Slang::createPreprocessor() {
221  // Default only search header file in current dir
222  clang::HeaderSearch *HS = new clang::HeaderSearch(*mFileMgr);
223
224  mPP.reset(new clang::Preprocessor(*mDiagnostics,
225                                    LangOpts,
226                                    *mTarget,
227                                    *mSourceMgr,
228                                    *HS,
229                                    NULL,
230                                    /* OwnsHeaderSearch = */true));
231  // Initialize the prepocessor
232  mPragmas.clear();
233  mPP->AddPragmaHandler(new PragmaRecorder(mPragmas));
234
235  std::vector<clang::DirectoryLookup> SearchList;
236  for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) {
237    if (const clang::DirectoryEntry *DE =
238            mFileMgr->getDirectory(mIncludePaths[i])) {
239      SearchList.push_back(clang::DirectoryLookup(DE,
240                                                  clang::SrcMgr::C_System,
241                                                  false,
242                                                  false));
243    }
244  }
245
246  HS->SetSearchPaths(SearchList, 1, false);
247
248  initPreprocessor();
249  return;
250}
251
252void Slang::createASTContext() {
253  mASTContext.reset(new clang::ASTContext(LangOpts,
254                                          *mSourceMgr,
255                                          *mTarget,
256                                          mPP->getIdentifierTable(),
257                                          mPP->getSelectorTable(),
258                                          mPP->getBuiltinInfo(),
259                                          /* size_reserve = */0));
260  initASTContext();
261  return;
262}
263
264clang::ASTConsumer
265*Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts,
266                      llvm::raw_ostream *OS,
267                      OutputType OT) {
268  return new Backend(*mDiagnostics,
269                     CodeGenOpts,
270                     mTargetOpts,
271                     mPragmas,
272                     OS,
273                     OT);
274}
275
276Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) {
277  GlobalInitialization();
278  return;
279}
280
281void Slang::init(const std::string &Triple, const std::string &CPU,
282                 const std::vector<std::string> &Features) {
283  if (mInitialized)
284    return;
285
286  createDiagnostic();
287  llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagnostics.getPtr());
288
289  createTarget(Triple, CPU, Features);
290  createFileManager();
291  createSourceManager();
292
293  mInitialized = true;
294
295  return;
296}
297
298bool Slang::setInputSource(llvm::StringRef InputFile,
299                           const char *Text,
300                           size_t TextLength) {
301  mInputFileName = InputFile.str();
302
303  // Reset the ID tables if we are reusing the SourceManager
304  mSourceMgr->clearIDTables();
305
306  // Load the source
307  llvm::MemoryBuffer *SB =
308      llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength);
309  mSourceMgr->createMainFileIDForMemBuffer(SB);
310
311  if (mSourceMgr->getMainFileID().isInvalid()) {
312    mDiagnostics->Report(clang::diag::err_fe_error_reading) << InputFile;
313    return false;
314  }
315  return true;
316}
317
318bool Slang::setInputSource(llvm::StringRef InputFile) {
319  mInputFileName = InputFile.str();
320
321  mSourceMgr->clearIDTables();
322
323  const clang::FileEntry *File = mFileMgr->getFile(InputFile);
324  if (File)
325    mSourceMgr->createMainFileID(File);
326
327  if (mSourceMgr->getMainFileID().isInvalid()) {
328    mDiagnostics->Report(clang::diag::err_fe_error_reading) << InputFile;
329    return false;
330  }
331
332  return true;
333}
334
335bool Slang::setOutput(const char *OutputFile) {
336  llvm::sys::Path OutputFilePath(OutputFile);
337  std::string Error;
338  llvm::tool_output_file *OS = NULL;
339
340  switch (mOT) {
341    case OT_Dependency:
342    case OT_Assembly:
343    case OT_LLVMAssembly: {
344      OS = OpenOutputFile(OutputFile, 0, &Error, mDiagnostics.getPtr());
345      break;
346    }
347    case OT_Nothing: {
348      break;
349    }
350    case OT_Object:
351    case OT_Bitcode: {
352      OS = OpenOutputFile(OutputFile,
353                          llvm::raw_fd_ostream::F_Binary,
354                          &Error,
355                          mDiagnostics.getPtr());
356      break;
357    }
358    default: {
359      llvm_unreachable("Unknown compiler output type");
360    }
361  }
362
363  if (!Error.empty())
364    return false;
365
366  mOS.reset(OS);
367
368  mOutputFileName = OutputFile;
369
370  return true;
371}
372
373bool Slang::setDepOutput(const char *OutputFile) {
374  llvm::sys::Path OutputFilePath(OutputFile);
375  std::string Error;
376
377  mDOS.reset(OpenOutputFile(OutputFile, 0, &Error, mDiagnostics.getPtr()));
378  if (!Error.empty() || (mDOS.get() == NULL))
379    return false;
380
381  mDepOutputFileName = OutputFile;
382
383  return true;
384}
385
386int Slang::generateDepFile() {
387  if (mDiagnostics->getNumErrors() > 0)
388    return mDiagnostics->getNumErrors();
389  if (mDOS.get() == NULL)
390    return 1;
391
392  // Initialize options for generating dependency file
393  clang::DependencyOutputOptions DepOpts;
394  DepOpts.IncludeSystemHeaders = 1;
395  DepOpts.OutputFile = mDepOutputFileName;
396  DepOpts.Targets = mAdditionalDepTargets;
397  DepOpts.Targets.push_back(mDepTargetBCFileName);
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.get(), 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