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