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