slang.cpp revision 43730fe3c839af391efe6bdf56b0479860121924
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/Path.h"
63#include "llvm/Support/TargetSelect.h"
64#include "llvm/Support/ToolOutputFile.h"
65
66#include "slang_assert.h"
67#include "slang_backend.h"
68#include "slang_utils.h"
69
70namespace {
71
72struct ForceSlangLinking {
73  ForceSlangLinking() {
74    // We must reference the functions in such a way that compilers will not
75    // delete it all as dead code, even with whole program optimization,
76    // yet is effectively a NO-OP. As the compiler isn't smart enough
77    // to know that getenv() never returns -1, this will do the job.
78    if (std::getenv("bar") != reinterpret_cast<char*>(-1))
79      return;
80
81    // llvm-rs-link needs following functions existing in libslang.
82    llvm::ParseBitcodeFile(NULL, llvm::getGlobalContext(), NULL);
83    llvm::Linker::LinkModules(NULL, NULL, 0, NULL);
84
85    // llvm-rs-cc need this.
86    new clang::TextDiagnosticPrinter(llvm::errs(),
87                                     clang::DiagnosticOptions());
88  }
89} ForceSlangLinking;
90
91}  // namespace
92
93namespace slang {
94
95#if defined(__arm__)
96#   define DEFAULT_TARGET_TRIPLE_STRING "armv7-none-linux-gnueabi"
97#elif defined(__x86_64__)
98#   define DEFAULT_TARGET_TRIPLE_STRING "x86_64-unknown-linux"
99#else
100// let's use x86 as default target
101#   define DEFAULT_TARGET_TRIPLE_STRING "i686-unknown-linux"
102#endif
103
104bool Slang::GlobalInitialized = false;
105
106// Language option (define the language feature for compiler such as C99)
107clang::LangOptions Slang::LangOpts;
108
109// Code generation option for the compiler
110clang::CodeGenOptions Slang::CodeGenOpts;
111
112// The named of metadata node that pragma resides (should be synced with
113// bcc.cpp)
114const llvm::StringRef Slang::PragmaMetadataName = "#pragma";
115
116static inline llvm::tool_output_file *
117OpenOutputFile(const char *OutputFile,
118               unsigned Flags,
119               std::string* Error,
120               clang::DiagnosticsEngine *DiagEngine) {
121  slangAssert((OutputFile != NULL) && (Error != NULL) &&
122              (DiagEngine != NULL) && "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  DiagEngine->Report(clang::diag::err_fe_error_opening)
134    << OutputFile << *Error;
135
136  return NULL;
137}
138
139void Slang::GlobalInitialization() {
140  if (!GlobalInitialized) {
141    // We only support x86, x64 and ARM target
142
143    // For ARM
144    LLVMInitializeARMTargetInfo();
145    LLVMInitializeARMTarget();
146    LLVMInitializeARMAsmPrinter();
147
148    // For x86 and x64
149    LLVMInitializeX86TargetInfo();
150    LLVMInitializeX86Target();
151    LLVMInitializeX86AsmPrinter();
152
153    // Please refer to include/clang/Basic/LangOptions.h to setup
154    // the options.
155    LangOpts.RTTI = 0;  // Turn off the RTTI information support
156    LangOpts.C99 = 1;
157    LangOpts.Renderscript = 1;
158    LangOpts.CharIsSigned = 1;  // Signed char is our default.
159
160    CodeGenOpts.OptimizationLevel = 3;
161
162    GlobalInitialized = true;
163  }
164}
165
166void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) {
167  clang::DiagnosticsEngine* DiagEngine =
168    static_cast<clang::DiagnosticsEngine *>(UserData);
169
170  DiagEngine->Report(clang::diag::err_fe_error_backend) << Message;
171  exit(1);
172}
173
174void Slang::createTarget(const std::string &Triple, const std::string &CPU,
175                         const std::vector<std::string> &Features) {
176  if (!Triple.empty())
177    mTargetOpts.Triple = Triple;
178  else
179    mTargetOpts.Triple = DEFAULT_TARGET_TRIPLE_STRING;
180
181  if (!CPU.empty())
182    mTargetOpts.CPU = CPU;
183
184  if (!Features.empty())
185    mTargetOpts.Features = Features;
186
187  mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine,
188                                                    mTargetOpts));
189}
190
191void Slang::createFileManager() {
192  mFileSysOpt.reset(new clang::FileSystemOptions());
193  mFileMgr.reset(new clang::FileManager(*mFileSysOpt));
194}
195
196void Slang::createSourceManager() {
197  mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr));
198}
199
200void Slang::createPreprocessor() {
201  // Default only search header file in current dir
202  clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(*mFileMgr,
203                                                            *mDiagEngine,
204                                                            LangOpts,
205                                                            mTarget.get());
206
207  mPP.reset(new clang::Preprocessor(*mDiagEngine,
208                                    LangOpts,
209                                    mTarget.get(),
210                                    *mSourceMgr,
211                                    *HeaderInfo,
212                                    *this,
213                                    NULL,
214                                    /* OwnsHeaderSearch = */true));
215  // Initialize the preprocessor
216  mPragmas.clear();
217  mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas));
218
219  std::vector<clang::DirectoryLookup> SearchList;
220  for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) {
221    if (const clang::DirectoryEntry *DE =
222            mFileMgr->getDirectory(mIncludePaths[i])) {
223      SearchList.push_back(clang::DirectoryLookup(DE,
224                                                  clang::SrcMgr::C_System,
225                                                  false,
226                                                  false));
227    }
228  }
229
230  HeaderInfo->SetSearchPaths(SearchList,
231                             /* angledDirIdx = */1,
232                             /* systemDixIdx = */1,
233                             /* noCurDirSearch = */false);
234
235  initPreprocessor();
236}
237
238void Slang::createASTContext() {
239  mASTContext.reset(new clang::ASTContext(LangOpts,
240                                          *mSourceMgr,
241                                          mTarget.get(),
242                                          mPP->getIdentifierTable(),
243                                          mPP->getSelectorTable(),
244                                          mPP->getBuiltinInfo(),
245                                          /* size_reserve = */0));
246  initASTContext();
247}
248
249clang::ASTConsumer *
250Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts,
251                     llvm::raw_ostream *OS, OutputType OT) {
252  return new Backend(mDiagEngine, CodeGenOpts, mTargetOpts,
253                     &mPragmas, OS, OT);
254}
255
256Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) {
257  GlobalInitialization();
258}
259
260void Slang::init(const std::string &Triple, const std::string &CPU,
261                 const std::vector<std::string> &Features,
262                 clang::DiagnosticsEngine *DiagEngine,
263                 DiagnosticBuffer *DiagClient) {
264  if (mInitialized)
265    return;
266
267  mDiagEngine = DiagEngine;
268  mDiagClient = DiagClient;
269  mDiag.reset(new clang::Diagnostic(mDiagEngine));
270  initDiagnostic();
271  llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine);
272
273  createTarget(Triple, CPU, Features);
274  createFileManager();
275  createSourceManager();
276
277  mInitialized = true;
278}
279
280clang::Module *Slang::loadModule(clang::SourceLocation ImportLoc,
281                                 clang::ModuleIdPath Path,
282                                 clang::Module::NameVisibilityKind Visibility,
283                                 bool IsInclusionDirective) {
284  slangAssert(0 && "Not implemented");
285  return NULL;
286}
287
288bool Slang::setInputSource(llvm::StringRef InputFile,
289                           const char *Text,
290                           size_t TextLength) {
291  mInputFileName = InputFile.str();
292
293  // Reset the ID tables if we are reusing the SourceManager
294  mSourceMgr->clearIDTables();
295
296  // Load the source
297  llvm::MemoryBuffer *SB =
298      llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength);
299  mSourceMgr->createMainFileIDForMemBuffer(SB);
300
301  if (mSourceMgr->getMainFileID().isInvalid()) {
302    mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
303    return false;
304  }
305  return true;
306}
307
308bool Slang::setInputSource(llvm::StringRef InputFile) {
309  mInputFileName = InputFile.str();
310
311  mSourceMgr->clearIDTables();
312
313  const clang::FileEntry *File = mFileMgr->getFile(InputFile);
314  if (File)
315    mSourceMgr->createMainFileID(File);
316
317  if (mSourceMgr->getMainFileID().isInvalid()) {
318    mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
319    return false;
320  }
321
322  return true;
323}
324
325bool Slang::setOutput(const char *OutputFile) {
326  llvm::sys::Path OutputFilePath(OutputFile);
327  std::string Error;
328  llvm::tool_output_file *OS = NULL;
329
330  switch (mOT) {
331    case OT_Dependency:
332    case OT_Assembly:
333    case OT_LLVMAssembly: {
334      OS = OpenOutputFile(OutputFile, 0, &Error, mDiagEngine);
335      break;
336    }
337    case OT_Nothing: {
338      break;
339    }
340    case OT_Object:
341    case OT_Bitcode: {
342      OS = OpenOutputFile(OutputFile, llvm::raw_fd_ostream::F_Binary,
343                          &Error, mDiagEngine);
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, mDiagEngine));
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 (mDiagEngine->hasErrorOccurred())
376    return 1;
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 (!mDiagEngine->hasErrorOccurred())
412    mDOS->keep();
413
414  // Clean up after compilation
415  mPP.reset();
416  mDOS.reset();
417
418  return mDiagEngine->hasErrorOccurred() ? 1 : 0;
419}
420
421int Slang::compile() {
422  if (mDiagEngine->hasErrorOccurred())
423    return 1;
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->os(), 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 (!mDiagEngine->hasErrorOccurred())
444    mOS->keep();
445
446  // The compilation ended, clear
447  mBackend.reset();
448  mASTContext.reset();
449  mPP.reset();
450  mOS.reset();
451
452  return mDiagEngine->hasErrorOccurred() ? 1 : 0;
453}
454
455void Slang::setDebugMetadataEmission(bool EmitDebug) {
456  if (EmitDebug)
457    CodeGenOpts.DebugInfo = clang::CodeGenOptions::FullDebugInfo;
458  else
459    CodeGenOpts.DebugInfo = clang::CodeGenOptions::NoDebugInfo;
460}
461
462void Slang::setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel) {
463  CodeGenOpts.OptimizationLevel = OptimizationLevel;
464}
465
466void Slang::reset() {
467  llvm::errs() << mDiagClient->str();
468  mDiagEngine->Reset();
469  mDiagClient->reset();
470}
471
472Slang::~Slang() {
473  llvm::llvm_shutdown();
474}
475
476}  // namespace slang
477