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