/*
* Copyright 2010, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "slang.h"
#include <stdlib.h>
#include <string>
#include <vector>
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Frontend/CodeGenOptions.h"
#include "clang/Frontend/DependencyOutputOptions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Parse/ParseAST.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Bitcode/ReaderWriter.h"
// More force linking
#include "llvm/Linker.h"
// Force linking all passes/vmcore stuffs to libslang.so
#include "llvm/LinkAllIR.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "slang_assert.h"
#include "slang_backend.h"
#include "slang_utils.h"
namespace {
struct ForceSlangLinking {
ForceSlangLinking() {
// We must reference the functions in such a way that compilers will not
// delete it all as dead code, even with whole program optimization,
// yet is effectively a NO-OP. As the compiler isn't smart enough
// to know that getenv() never returns -1, this will do the job.
if (std::getenv("bar") != reinterpret_cast<char*>(-1))
return;
// llvm-rs-link needs following functions existing in libslang.
llvm::ParseBitcodeFile(NULL, llvm::getGlobalContext(), NULL);
llvm::Linker::LinkModules(NULL, NULL, 0, NULL);
// llvm-rs-cc need this.
new clang::TextDiagnosticPrinter(llvm::errs(),
new clang::DiagnosticOptions());
}
} ForceSlangLinking;
} // namespace
namespace slang {
#if defined(__arm__)
# define DEFAULT_TARGET_TRIPLE_STRING "armv7-none-linux-gnueabi"
#elif defined(__x86_64__)
# define DEFAULT_TARGET_TRIPLE_STRING "x86_64-unknown-linux"
#else
// let's use x86 as default target
# define DEFAULT_TARGET_TRIPLE_STRING "i686-unknown-linux"
#endif
bool Slang::GlobalInitialized = false;
// Language option (define the language feature for compiler such as C99)
clang::LangOptions Slang::LangOpts;
// Code generation option for the compiler
clang::CodeGenOptions Slang::CodeGenOpts;
// The named of metadata node that pragma resides (should be synced with
// bcc.cpp)
const llvm::StringRef Slang::PragmaMetadataName = "#pragma";
static inline llvm::tool_output_file *
OpenOutputFile(const char *OutputFile,
unsigned Flags,
std::string* Error,
clang::DiagnosticsEngine *DiagEngine) {
slangAssert((OutputFile != NULL) && (Error != NULL) &&
(DiagEngine != NULL) && "Invalid parameter!");
if (SlangUtils::CreateDirectoryWithParents(
llvm::sys::path::parent_path(OutputFile), Error)) {
llvm::tool_output_file *F =
new llvm::tool_output_file(OutputFile, *Error, Flags);
if (F != NULL)
return F;
}
// Report error here.
DiagEngine->Report(clang::diag::err_fe_error_opening)
<< OutputFile << *Error;
return NULL;
}
void Slang::GlobalInitialization() {
if (!GlobalInitialized) {
// We only support x86, x64 and ARM target
// For ARM
LLVMInitializeARMTargetInfo();
LLVMInitializeARMTarget();
LLVMInitializeARMAsmPrinter();
// For x86 and x64
LLVMInitializeX86TargetInfo();
LLVMInitializeX86Target();
LLVMInitializeX86AsmPrinter();
// Please refer to include/clang/Basic/LangOptions.h to setup
// the options.
LangOpts.RTTI = 0; // Turn off the RTTI information support
LangOpts.C99 = 1;
LangOpts.Renderscript = 1;
LangOpts.CharIsSigned = 1; // Signed char is our default.
CodeGenOpts.OptimizationLevel = 3;
GlobalInitialized = true;
}
}
void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) {
clang::DiagnosticsEngine* DiagEngine =
static_cast<clang::DiagnosticsEngine *>(UserData);
DiagEngine->Report(clang::diag::err_fe_error_backend) << Message;
exit(1);
}
void Slang::createTarget(const std::string &Triple, const std::string &CPU,
const std::vector<std::string> &Features) {
if (!Triple.empty())
mTargetOpts->Triple = Triple;
else
mTargetOpts->Triple = DEFAULT_TARGET_TRIPLE_STRING;
if (!CPU.empty())
mTargetOpts->CPU = CPU;
if (!Features.empty())
mTargetOpts->FeaturesAsWritten = Features;
mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine,
mTargetOpts.getPtr()));
}
void Slang::createFileManager() {
mFileSysOpt.reset(new clang::FileSystemOptions());
mFileMgr.reset(new clang::FileManager(*mFileSysOpt));
}
void Slang::createSourceManager() {
mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr));
}
void Slang::createPreprocessor() {
// Default only search header file in current dir
llvm::IntrusiveRefCntPtr<clang::HeaderSearchOptions> HSOpts =
new clang::HeaderSearchOptions();
clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(HSOpts,
*mFileMgr,
*mDiagEngine,
LangOpts,
mTarget.get());
llvm::IntrusiveRefCntPtr<clang::PreprocessorOptions> PPOpts =
new clang::PreprocessorOptions();
mPP.reset(new clang::Preprocessor(PPOpts,
*mDiagEngine,
LangOpts,
mTarget.get(),
*mSourceMgr,
*HeaderInfo,
*this,
NULL,
/* OwnsHeaderSearch = */true));
// Initialize the preprocessor
mPragmas.clear();
mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas));
std::vector<clang::DirectoryLookup> SearchList;
for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) {
if (const clang::DirectoryEntry *DE =
mFileMgr->getDirectory(mIncludePaths[i])) {
SearchList.push_back(clang::DirectoryLookup(DE,
clang::SrcMgr::C_System,
false));
}
}
HeaderInfo->SetSearchPaths(SearchList,
/* angledDirIdx = */1,
/* systemDixIdx = */1,
/* noCurDirSearch = */false);
initPreprocessor();
}
void Slang::createASTContext() {
mASTContext.reset(new clang::ASTContext(LangOpts,
*mSourceMgr,
mTarget.get(),
mPP->getIdentifierTable(),
mPP->getSelectorTable(),
mPP->getBuiltinInfo(),
/* size_reserve = */0));
initASTContext();
}
clang::ASTConsumer *
Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts,
llvm::raw_ostream *OS, OutputType OT) {
return new Backend(mDiagEngine, CodeGenOpts, getTargetOptions(),
&mPragmas, OS, OT);
}
Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) {
mTargetOpts = new clang::TargetOptions();
GlobalInitialization();
}
void Slang::init(const std::string &Triple, const std::string &CPU,
const std::vector<std::string> &Features,
clang::DiagnosticsEngine *DiagEngine,
DiagnosticBuffer *DiagClient) {
if (mInitialized)
return;
mDiagEngine = DiagEngine;
mDiagClient = DiagClient;
mDiag.reset(new clang::Diagnostic(mDiagEngine));
initDiagnostic();
llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine);
createTarget(Triple, CPU, Features);
createFileManager();
createSourceManager();
mInitialized = true;
}
clang::ModuleLoadResult Slang::loadModule(
clang::SourceLocation ImportLoc,
clang::ModuleIdPath Path,
clang::Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) {
slangAssert(0 && "Not implemented");
return clang::ModuleLoadResult();
}
bool Slang::setInputSource(llvm::StringRef InputFile,
const char *Text,
size_t TextLength) {
mInputFileName = InputFile.str();
// Reset the ID tables if we are reusing the SourceManager
mSourceMgr->clearIDTables();
// Load the source
llvm::MemoryBuffer *SB =
llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength);
mSourceMgr->createMainFileIDForMemBuffer(SB);
if (mSourceMgr->getMainFileID().isInvalid()) {
mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
return false;
}
return true;
}
bool Slang::setInputSource(llvm::StringRef InputFile) {
mInputFileName = InputFile.str();
mSourceMgr->clearIDTables();
const clang::FileEntry *File = mFileMgr->getFile(InputFile);
if (File)
mSourceMgr->createMainFileID(File);
if (mSourceMgr->getMainFileID().isInvalid()) {
mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
return false;
}
return true;
}
bool Slang::setOutput(const char *OutputFile) {
llvm::sys::Path OutputFilePath(OutputFile);
std::string Error;
llvm::tool_output_file *OS = NULL;
switch (mOT) {
case OT_Dependency:
case OT_Assembly:
case OT_LLVMAssembly: {
OS = OpenOutputFile(OutputFile, 0, &Error, mDiagEngine);
break;
}
case OT_Nothing: {
break;
}
case OT_Object:
case OT_Bitcode: {
OS = OpenOutputFile(OutputFile, llvm::raw_fd_ostream::F_Binary,
&Error, mDiagEngine);
break;
}
default: {
llvm_unreachable("Unknown compiler output type");
}
}
if (!Error.empty())
return false;
mOS.reset(OS);
mOutputFileName = OutputFile;
return true;
}
bool Slang::setDepOutput(const char *OutputFile) {
llvm::sys::Path OutputFilePath(OutputFile);
std::string Error;
mDOS.reset(OpenOutputFile(OutputFile, 0, &Error, mDiagEngine));
if (!Error.empty() || (mDOS.get() == NULL))
return false;
mDepOutputFileName = OutputFile;
return true;
}
int Slang::generateDepFile() {
if (mDiagEngine->hasErrorOccurred())
return 1;
if (mDOS.get() == NULL)
return 1;
// Initialize options for generating dependency file
clang::DependencyOutputOptions DepOpts;
DepOpts.IncludeSystemHeaders = 1;
DepOpts.OutputFile = mDepOutputFileName;
DepOpts.Targets = mAdditionalDepTargets;
DepOpts.Targets.push_back(mDepTargetBCFileName);
for (std::vector<std::string>::const_iterator
I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end();
I != E;
I++) {
DepOpts.Targets.push_back(*I);
}
mGeneratedFileNames.clear();
// Per-compilation needed initialization
createPreprocessor();
AttachDependencyFileGen(*mPP.get(), DepOpts);
// Inform the diagnostic client we are processing a source file
mDiagClient->BeginSourceFile(LangOpts, mPP.get());
// Go through the source file (no operations necessary)
clang::Token Tok;
mPP->EnterMainSourceFile();
do {
mPP->Lex(Tok);
} while (Tok.isNot(clang::tok::eof));
mPP->EndSourceFile();
// Declare success if no error
if (!mDiagEngine->hasErrorOccurred())
mDOS->keep();
// Clean up after compilation
mPP.reset();
mDOS.reset();
return mDiagEngine->hasErrorOccurred() ? 1 : 0;
}
int Slang::compile() {
if (mDiagEngine->hasErrorOccurred())
return 1;
if (mOS.get() == NULL)
return 1;
// Here is per-compilation needed initialization
createPreprocessor();
createASTContext();
mBackend.reset(createBackend(CodeGenOpts, &mOS->os(), mOT));
// Inform the diagnostic client we are processing a source file
mDiagClient->BeginSourceFile(LangOpts, mPP.get());
// The core of the slang compiler
ParseAST(*mPP, mBackend.get(), *mASTContext);
// Inform the diagnostic client we are done with previous source file
mDiagClient->EndSourceFile();
// Declare success if no error
if (!mDiagEngine->hasErrorOccurred())
mOS->keep();
// The compilation ended, clear
mBackend.reset();
mASTContext.reset();
mPP.reset();
mOS.reset();
return mDiagEngine->hasErrorOccurred() ? 1 : 0;
}
void Slang::setDebugMetadataEmission(bool EmitDebug) {
if (EmitDebug)
CodeGenOpts.setDebugInfo(clang::CodeGenOptions::FullDebugInfo);
else
CodeGenOpts.setDebugInfo(clang::CodeGenOptions::NoDebugInfo);
}
void Slang::setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel) {
CodeGenOpts.OptimizationLevel = OptimizationLevel;
}
void Slang::reset() {
llvm::errs() << mDiagClient->str();
mDiagEngine->Reset();
mDiagClient->reset();
}
Slang::~Slang() {
llvm::llvm_shutdown();
}
} // namespace slang