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