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