diff options
author | Andrew Hsieh <andrewhsieh@google.com> | 2012-07-31 15:54:16 -0700 |
---|---|---|
committer | Andrew Hsieh <andrewhsieh@google.com> | 2012-07-31 15:54:16 -0700 |
commit | 42552ec6168d994991696a04c1626ac91218a720 (patch) | |
tree | 33fb886ae12c33dc1d0d32130a2c7d0b60572fed | |
parent | 8ad720504e84a65fc3d01947a2effed67ad47346 (diff) | |
download | llvm-ndk-cc-42552ec6168d994991696a04c1626ac91218a720.tar.gz |
llvm-ndk-cc: driver of NDK llvm ABI
Change-Id: I6b230d089fd7f3b944504b0a1f9a980cc3524aec
30 files changed, 1583 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..0e28e75 --- /dev/null +++ b/Android.mk @@ -0,0 +1,145 @@ +LOCAL_PATH := $(call my-dir) +LLVM_ROOT_PATH := external/llvm +CLANG_ROOT_PATH := external/clang +llvm_static_libs_needed := \ + libLLVMLinker \ + libLLVMipo \ + libLLVMBitWriter \ + libLLVMBitReader \ + libLLVMARMCodeGen \ + libLLVMARMAsmPrinter \ + libLLVMARMInfo \ + libLLVMX86CodeGen \ + libLLVMX86AsmPrinter \ + libLLVMX86Info \ + libLLVMX86Utils \ + libLLVMAsmPrinter \ + libLLVMSelectionDAG \ + libLLVMCodeGen \ + libLLVMScalarOpts \ + libLLVMInstCombine \ + libLLVMTransformUtils \ + libLLVMInstrumentation \ + libLLVMipa \ + libLLVMAnalysis \ + libLLVMTarget \ + libLLVMMC \ + libLLVMMCParser \ + libLLVMCore \ + libclangFrontend \ + libclangDriver \ + libclangSerialization \ + libclangCodeGen \ + libclangParse \ + libclangSema \ + libclangAnalysis \ + libclangEdit \ + libclangAST \ + libclangLex \ + libclangBasic \ + libLLVMSupport \ + libLLVMVectorize + +# ======================================================== +# Static library libndkpc for host +# ======================================================== +include $(CLEAR_VARS) +include $(CLEAR_TBLGEN_VARS) + +include $(CLANG_ROOT_PATH)/clang.mk + +LOCAL_MODULE := libndkpc +LOCAL_MODULE_TAGS := optional + +LOCAL_CFLAGS += -Wno-sign-promo +ifneq ($(TARGET_BUILD_VARIANT),eng) +LOCAL_CFLAGS += -D__DISABLE_ASSERTS +endif + +TBLGEN_TABLES := \ + AttrList.inc \ + Attrs.inc \ + DeclNodes.inc \ + DiagnosticCommonKinds.inc \ + DiagnosticFrontendKinds.inc \ + DiagnosticSemaKinds.inc \ + StmtNodes.inc + +LOCAL_SRC_FILES := \ + Compiler.cpp \ + Backend.cpp + +LOCAL_LDLIBS := -ldl -lpthread + +include $(CLANG_HOST_BUILD_MK) +include $(CLANG_TBLGEN_RULES_MK) +include $(LLVM_GEN_INTRINSICS_MK) +include $(BUILD_HOST_STATIC_LIBRARY) + +# ======================================================== +# Executable llvm-ndk-cc for host +# ======================================================== +include $(CLEAR_VARS) +include $(CLEAR_TBLGEN_VARS) + +LOCAL_IS_HOST_MODULE := true +LOCAL_MODULE := llvm-ndk-cc +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE_CLASS := EXECUTABLES + +LOCAL_CFLAGS += -Wno-sign-promo +ifneq ($(TARGET_BUILD_VARIANT),eng) +LOCAL_CFLAGS += -D__DISABLE_ASSERTS +endif + +TBLGEN_TABLES := \ + AttrList.inc \ + Attrs.inc \ + DeclNodes.inc \ + DiagnosticCommonKinds.inc \ + DiagnosticDriverKinds.inc \ + DiagnosticFrontendKinds.inc \ + DiagnosticSemaKinds.inc \ + StmtNodes.inc + +LOCAL_SRC_FILES := \ + llvm-ndk-cc.cpp + +LOCAL_STATIC_LIBRARIES := \ + libclangDriver libndkpc \ + $(llvm_static_libs_needed) + +ifeq ($(HOST_OS),windows) + LOCAL_LDLIBS := -limagehlp -lpsapi +else + LOCAL_LDLIBS := -ldl -lpthread +endif + +include $(CLANG_HOST_BUILD_MK) +include $(CLANG_TBLGEN_RULES_MK) +include $(BUILD_HOST_EXECUTABLE) + +# ======================================================== +# Executable llvm-ndk-link for host +# ======================================================== +include $(CLEAR_VARS) + +LOCAL_IS_HOST_MODULE := true +LOCAL_MODULE := llvm-ndk-link +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := EXECUTABLES + +LOCAL_SRC_FILES := llvm-ndk-link.cpp + +LOCAL_LDLIBS := -ldl -lpthread + +LOCAL_STATIC_LIBRARIES := \ + $(llvm_static_libs_needed) + +include $(CLANG_ROOT_PATH)/clang.mk +include $(CLANG_HOST_BUILD_MK) +include $(LLVM_ROOT_PATH)/llvm.mk +include $(LLVM_HOST_BUILD_MK) + +include $(BUILD_HOST_EXECUTABLE) diff --git a/Backend.cpp b/Backend.cpp new file mode 100644 index 0000000..57dd54b --- /dev/null +++ b/Backend.cpp @@ -0,0 +1,334 @@ +#include "Backend.h" + +#include <cassert> +#include <string> +#include <vector> + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" + +#include "clang/CodeGen/ModuleBuilder.h" + +#include "clang/Frontend/CodeGenOptions.h" +#include "clang/Frontend/FrontendDiagnostic.h" + +#include "llvm/Assembly/PrintModulePass.h" + +#include "llvm/Bitcode/ReaderWriter.h" + +#include "llvm/CodeGen/RegAllocRegistry.h" +#include "llvm/CodeGen/SchedulerRegistry.h" + +#include "llvm/Instructions.h" +#include "llvm/LLVMContext.h" +#include "llvm/Module.h" +#include "llvm/Metadata.h" + +#include "llvm/MC/SubtargetFeature.h" + +#include "llvm/Support/Casting.h" +#include "llvm/Support/InstIterator.h" + +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/Support/TargetRegistry.h" + +namespace ndkpc { + +void Backend::CreateFunctionPasses() { + if (!mpPerFunctionPasses) { + mpPerFunctionPasses = new llvm::FunctionPassManager(mpModule); + mpPerFunctionPasses->add(new llvm::TargetData(mpModule)); + + // FIXME REMOVE + //llvm::createStandardFunctionPasses(mpPerFunctionPasses, + // mCodeGenOpts.OptimizationLevel); + llvm::PassManagerBuilder PMBuilder; + PMBuilder.OptLevel = mCodeGenOpts.OptimizationLevel; + PMBuilder.populateFunctionPassManager(*mpPerFunctionPasses); + } + return; +} + +void Backend::CreateModulePasses() { + if (!mpPerModulePasses) { + mpPerModulePasses = new llvm::PassManager(); + mpPerModulePasses->add(new llvm::TargetData(mpModule)); + + llvm::PassManagerBuilder PMBuilder; + PMBuilder.OptLevel = mCodeGenOpts.OptimizationLevel; + PMBuilder.SizeLevel = mCodeGenOpts.OptimizeSize; + if (mCodeGenOpts.UnitAtATime) { + PMBuilder.DisableUnitAtATime = 0; + } else { + PMBuilder.DisableUnitAtATime = 1; + } + + if (mCodeGenOpts.UnrollLoops) { + PMBuilder.DisableUnrollLoops = 0; + } else { + PMBuilder.DisableUnrollLoops = 1; + } + + PMBuilder.DisableSimplifyLibCalls = false; + PMBuilder.populateModulePassManager(*mpPerModulePasses); + } + return; +} + +bool Backend::CreateCodeGenPasses() { + if ((mOT != Compiler::OT_Assembly) && (mOT != Compiler::OT_Object)) + return true; + + // Now we add passes for code emitting + if (mpCodeGenPasses) { + return true; + } else { + mpCodeGenPasses = new llvm::FunctionPassManager(mpModule); + mpCodeGenPasses->add(new llvm::TargetData(mpModule)); + } + + // Create the TargetMachine for generating code. + std::string Triple = mpModule->getTargetTriple(); + + std::string Error; + const llvm::Target* TargetInfo = + llvm::TargetRegistry::lookupTarget(Triple, Error); + if (TargetInfo == NULL) { + mDiags.Report(clang::diag::err_fe_unable_to_create_target) << Error; + return false; + } + + llvm::TargetOptions Options; + Options.NoFramePointerElim = mCodeGenOpts.DisableFPElim; + + // Use hardware FPU. + // + // FIXME: Need to detect the CPU capability and decide whether to use softfp. + // To use softfp, change following 2 lines to + // + // llvm::FloatABIType = llvm::FloatABI::Soft; + // llvm::UseSoftFloat = true; + Options.FloatABIType = llvm::FloatABI::Hard; + Options.UseSoftFloat = false; + + // BCC needs all unknown symbols resolved at compilation time. So we don't + // need any relocation model. + llvm::Reloc::Model RelocModel = llvm::Reloc::Static; + + // This is set for the linker (specify how large of the virtual addresses we + // can access for all unknown symbols.) + llvm::CodeModel::Model CodeModel; + if (mpModule->getPointerSize() == llvm::Module::Pointer32) + CodeModel = llvm::CodeModel::Small; + else + // The target may have pointer size greater than 32 (e.g. x86_64 + // architecture) may need large data address model + CodeModel = llvm::CodeModel::Medium; + + // Setup feature string + std::string FeaturesStr; + if (mTargetOpts.CPU.size() || mTargetOpts.Features.size()) { + llvm::SubtargetFeatures Features; + + for (std::vector<std::string>::const_iterator + I = mTargetOpts.Features.begin(), E = mTargetOpts.Features.end(); + I != E; + I++) + Features.AddFeature(*I); + + FeaturesStr = Features.getString(); + } + // Setup optimization level + llvm::CodeGenOpt::Level OptLevel = llvm::CodeGenOpt::Default; + if (mCodeGenOpts.OptimizationLevel == 0) + OptLevel = llvm::CodeGenOpt::None; + else if (mCodeGenOpts.OptimizationLevel == 3) + OptLevel = llvm::CodeGenOpt::Aggressive; + + llvm::TargetMachine *TM = + TargetInfo->createTargetMachine(Triple, mTargetOpts.CPU, FeaturesStr, + Options, RelocModel, CodeModel, OptLevel); + + // Register scheduler + llvm::RegisterScheduler::setDefault(llvm::createDefaultScheduler); + + // Register allocation policy: + // createFastRegisterAllocator: fast but bad quality + // createLinearScanRegisterAllocator: not so fast but good quality + llvm::RegisterRegAlloc::setDefault((mCodeGenOpts.OptimizationLevel == 0) ? + llvm::createFastRegisterAllocator : + llvm::createGreedyRegisterAllocator); + + llvm::TargetMachine::CodeGenFileType CGFT = + llvm::TargetMachine::CGFT_AssemblyFile; + if (mOT == Compiler::OT_Object) + CGFT = llvm::TargetMachine::CGFT_ObjectFile; + if (TM->addPassesToEmitFile(*mpCodeGenPasses, FormattedOutStream, + CGFT, OptLevel)) { + mDiags.Report(clang::diag::err_fe_unable_to_interface_with_target); + return false; + } + + return true; +} + +Backend::Backend(const clang::CodeGenOptions &CodeGenOpts, + const clang::TargetOptions &TargetOpts, + clang::DiagnosticsEngine *Diags, + llvm::raw_ostream *OS, + Compiler::OutputType OT) + : ASTConsumer(), + mCodeGenOpts(CodeGenOpts), + mTargetOpts(TargetOpts), + mLLVMContext(llvm::getGlobalContext()), + mDiags(*Diags), + mpModule(NULL), + mpOS(OS), + mOT(OT), + mpGen(NULL), + mpPerFunctionPasses(NULL), + mpPerModulePasses(NULL), + mpCodeGenPasses(NULL) { + FormattedOutStream.setStream(*mpOS, + llvm::formatted_raw_ostream::PRESERVE_STREAM); + mpGen = CreateLLVMCodeGen(mDiags, "", mCodeGenOpts, mLLVMContext); + return; +} + +void Backend::Initialize(clang::ASTContext &Ctx) { + mpGen->Initialize(Ctx); + mpModule = mpGen->GetModule(); + return; +} + +bool Backend::HandleTopLevelDecl(clang::DeclGroupRef D) { + mpGen->HandleTopLevelDecl(D); + return true; +} + +void Backend::HandleTranslationUnit(clang::ASTContext &Ctx) { + mpGen->HandleTranslationUnit(Ctx); + + // Here, we complete a translation unit (whole translation unit is now in LLVM + // IR). Now, interact with LLVM backend to generate actual machine code (asm + // or machine code, whatever.) + + // Silently ignore if we weren't initialized for some reason. + if (!mpModule) + return; + + llvm::Module *M = mpGen->ReleaseModule(); + if (!M) { + // The module has been released by IR gen on failures, do not double free. + mpModule = NULL; + return; + } + + assert(mpModule == M && + "Unexpected module change during LLVM IR generation"); + + // Handle illigal CallSite + for (llvm::Module::iterator I = mpModule->begin(), E = mpModule->end(); + I != E; + ++I) { + for (llvm::inst_iterator i = llvm::inst_begin(*I), e = llvm::inst_end(*I); + i != e; + ++i) { + if (llvm::CallInst* CallInst = llvm::dyn_cast<llvm::CallInst>(&*i)) { + if (CallInst->isInlineAsm()) { + // TODO: Should we reflect source location information to diagnostic + // class and show to users? + llvm::errs() << "Inline assembly is illigal. Please don't use it." << "\n"; + exit(1); + } + } + } + } + + // Create and run per-function passes + CreateFunctionPasses(); + if (mpPerFunctionPasses) { + mpPerFunctionPasses->doInitialization(); + + for (llvm::Module::iterator I = mpModule->begin(), E = mpModule->end(); + I != E; + I++) + if (!I->isDeclaration()) + mpPerFunctionPasses->run(*I); + + mpPerFunctionPasses->doFinalization(); + } + + // Create and run module passes + CreateModulePasses(); + if (mpPerModulePasses) + mpPerModulePasses->run(*mpModule); + + switch (mOT) { + case Compiler::OT_Assembly: + case Compiler::OT_Object: { + if (!CreateCodeGenPasses()) + return; + + mpCodeGenPasses->doInitialization(); + + for (llvm::Module::iterator I = mpModule->begin(), E = mpModule->end(); + I != E; + I++) + if (!I->isDeclaration()) + mpCodeGenPasses->run(*I); + + mpCodeGenPasses->doFinalization(); + break; + } + case Compiler::OT_LLVMAssembly: { + llvm::PassManager *LLEmitPM = new llvm::PassManager(); + LLEmitPM->add(llvm::createPrintModulePass(&FormattedOutStream)); + LLEmitPM->run(*mpModule); + break; + } + case Compiler::OT_Bitcode: { + llvm::PassManager *BCEmitPM = new llvm::PassManager(); + BCEmitPM->add(llvm::createBitcodeWriterPass(FormattedOutStream)); + BCEmitPM->run(*mpModule); + break; + } + case Compiler::OT_Nothing: { + return; + } + default: { + assert(false && "Unknown output type"); + } + } + + FormattedOutStream.flush(); + return; +} + +void Backend::HandleTagDeclDefinition(clang::TagDecl *D) { + mpGen->HandleTagDeclDefinition(D); + return; +} + +void Backend::CompleteTentativeDefinition(clang::VarDecl *D) { + mpGen->CompleteTentativeDefinition(D); + return; +} + +Backend::~Backend() { + delete mpModule; + delete mpGen; + delete mpPerFunctionPasses; + delete mpPerModulePasses; + delete mpCodeGenPasses; + return; +} + +} diff --git a/Backend.h b/Backend.h new file mode 100644 index 0000000..a6bb7f3 --- /dev/null +++ b/Backend.h @@ -0,0 +1,100 @@ +#ifndef _LLVM_NDK_CC_BACKEND_H +#define _LLVM_NDK_CC_BACKEND_H + +#include "clang/AST/ASTConsumer.h" + +#include "llvm/PassManager.h" + +#include "llvm/Support/FormattedStream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" + +#include "Compiler.h" + +namespace llvm { + class formatted_raw_ostream; + class LLVMContext; + class NamedMDNode; + class Module; + class PassManager; + class FunctionPassManager; +} + +namespace clang { + class CodeGenOptions; + class CodeGenerator; + class DeclGroupRef; + class TagDecl; + class VarDecl; +} + +namespace ndkpc { + +class Backend : public clang::ASTConsumer { +public: + Backend(const clang::CodeGenOptions &CodeGenOpts, + const clang::TargetOptions &TargetOpts, + clang::DiagnosticsEngine *Diags, + llvm::raw_ostream *OS, + Compiler::OutputType OT); + + virtual ~Backend(); + + // Initialize - This is called to initialize the consumer, providing the + // ASTContext. + virtual void Initialize(clang::ASTContext &Ctx); + + // HandleTopLevelDecl - Handle the specified top-level declaration. This is + // called by the parser to process every top-level Decl*. Note that D can be + // the head of a chain of Decls (e.g. for `int a, b` the chain will have two + // elements). Use Decl::getNextDeclarator() to walk the chain. + virtual bool HandleTopLevelDecl(clang::DeclGroupRef D); + + // HandleTranslationUnit - This method is called when the ASTs for entire + // translation unit have been parsed. + virtual void HandleTranslationUnit(clang::ASTContext &Ctx); + + // HandleTagDeclDefinition - This callback is invoked each time a TagDecl + // (e.g. struct, union, enum, class) is completed. This allows the client to + // hack on the type, which can occur at any point in the file (because these + // can be defined in declspecs). + virtual void HandleTagDeclDefinition(clang::TagDecl *D); + + // CompleteTentativeDefinition - Callback invoked at the end of a translation + // unit to notify the consumer that the given tentative definition should be + // completed. + virtual void CompleteTentativeDefinition(clang::VarDecl *D); + +private: + const clang::CodeGenOptions &mCodeGenOpts; + const clang::TargetOptions &mTargetOpts; + + llvm::LLVMContext &mLLVMContext; + clang::DiagnosticsEngine &mDiags; + + llvm::Module *mpModule; + + // Output stream + llvm::raw_ostream *mpOS; + Compiler::OutputType mOT; + + // This helps us translate Clang AST using into LLVM IR + clang::CodeGenerator *mpGen; + + // Passes apply on function scope in a translation unit + llvm::FunctionPassManager *mpPerFunctionPasses; + void CreateFunctionPasses(); + + // Passes apply on module scope + llvm::PassManager *mpPerModulePasses; + void CreateModulePasses(); + + // Passes for code emission + llvm::FunctionPassManager *mpCodeGenPasses; + bool CreateCodeGenPasses(); + + llvm::formatted_raw_ostream FormattedOutStream; +}; + +} + +#endif // _LLVM_NDK_CC_BACKEND_H diff --git a/Compiler.cpp b/Compiler.cpp new file mode 100644 index 0000000..f8bc250 --- /dev/null +++ b/Compiler.cpp @@ -0,0 +1,333 @@ +#include "Compiler.h" + +#include <cassert> +#include <cstdlib> +#include <string> +#include <vector> + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" + +#include "clang/Basic/DiagnosticIDs.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/DiagnosticOptions.h" +#include "clang/Frontend/DependencyOutputOptions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" + +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/HeaderSearch.h" + +#include "clang/Parse/ParseAST.h" + +#include "llvm/LLVMContext.h" + +#include "llvm/ADT/IntrusiveRefCntPtr.h" + +#include "llvm/Bitcode/ReaderWriter.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/ToolOutputFile.h" +#include "llvm/Support/Path.h" + +#include "llvm/Support/TargetSelect.h" + +#include "Backend.h" + +namespace ndkpc { + +static inline llvm::tool_output_file *openOutputFile(const char *OutputFile, + unsigned Flags, + std::string* Error, + clang::DiagnosticsEngine* Diag) { + assert((OutputFile != NULL) && (Error != NULL) && (Diag != NULL) && + "Invalid parameter!"); + + llvm::tool_output_file *F = + new llvm::tool_output_file(OutputFile, *Error, Flags); + if (F != NULL) + return F; + + // Report error here. + Diag->Report(clang::diag::err_fe_error_opening) << OutputFile << *Error; + + return NULL; +} + +void Compiler::LLVMErrorHandler(void *UserData, const std::string &Message) { + clang::DiagnosticsEngine* Diags = static_cast<clang::DiagnosticsEngine*>(UserData); + Diags->Report(clang::diag::err_fe_error_backend) << Message; + exit(1); +} + +void Compiler::createDiagnostic() { + mpDiagClient = new clang::TextDiagnosticPrinter(llvm::errs(), + clang::DiagnosticOptions()); + mDiagIDs = new clang::DiagnosticIDs(); + mDiagnostics = new clang::DiagnosticsEngine(mDiagIDs, mpDiagClient); + initDiagnostic(); + return; +} + +void Compiler::createTarget(const std::string &Triple, const std::string &CPU, + const std::vector<std::string> &Features) { + if (!Triple.empty()) + mTargetOpts.Triple = Triple; + else + mTargetOpts.Triple = llvm::Triple::normalize(DEFAULT_TARGET_TRIPLE_STRING); + + if (!CPU.empty()) + mTargetOpts.CPU = CPU; + + if (!Features.empty()) + mTargetOpts.Features = Features; + + mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagnostics, + mTargetOpts)); + + return; +} + +void Compiler::createFileManager() { + mFileSysOpt.reset(new clang::FileSystemOptions()); + mFileMgr.reset(new clang::FileManager(*mFileSysOpt)); +} + +void Compiler::createSourceManager() { + mSourceMgr.reset(new clang::SourceManager(*mDiagnostics, *mFileMgr)); + return; +} + +void Compiler::createPreprocessor() { + clang::HeaderSearch *HS = new clang::HeaderSearch(*mFileMgr, + *mDiagnostics, + mLangOpts, + mTarget.get()); + + llvm::OwningPtr<clang::CompilerInstance> Clang(new clang::CompilerInstance()); + + mPP.reset(new clang::Preprocessor(*mDiagnostics, + mLangOpts, + mTarget.get(), + *mSourceMgr, + *HS, + *Clang, + /* IILookup */0, + /* OwnsHeaderSearch = */true, + /*DelayInitialization=*/true)); + + 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, /* isUser */ + false /* isFramework */)); + } + } + + HS->SetSearchPaths(SearchList, 0/* angledDirIdx FIXME CHECK */, 0/* systemDirIdx */, false/* noCurDirSearch */); + + initPreprocessor(); + return; +} + +void Compiler::createASTContext() { + mASTContext.reset(new clang::ASTContext(mLangOpts, + *mSourceMgr, + mTarget.get(), + mPP->getIdentifierTable(), + mPP->getSelectorTable(), + mPP->getBuiltinInfo(), + /* size_reserve = */0, + /*DelayInitialization=*/true)); + initASTContext(); + return; +} + +clang::ASTConsumer +*Compiler::createBackend(const clang::CodeGenOptions& CodeGenOpts, + llvm::raw_ostream *OS, + OutputType OT) { + return new Backend(CodeGenOpts, + mTargetOpts, + mDiagnostics.getPtr(), + OS, + OT); +} + +Compiler::Compiler() : mInitialized(false), mpDiagClient(NULL), mOT(OT_Default) { +} + +void Compiler::injectPreDefined() { + typedef std::map<std::string, std::string> SymbolMapTy; + for (SymbolMapTy::iterator + it = mPreDefinedSymbolMap.begin(), et = mPreDefinedSymbolMap.end(); + it != et; ++it) { + std::string Str = "#define "+it->first+" "+it->second+"\n"; + mPP->setPredefines(Str); + } +} + +void Compiler::init(const std::string &Triple, const std::string &CPU, + const std::vector<std::string> &Features, bool isCXX) { + mLangOpts.RTTI = 0; // Turn off the RTTI information support + mLangOpts.NeXTRuntime = 0; // Turn off the NeXT runtime uses + mLangOpts.C99 = 1; + if (isCXX) { + mLangOpts.CPlusPlus = 1; + } + + mCodeGenOpts.OptimizationLevel = 3; /* -O3 */ + + createDiagnostic(); + llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagnostics.getPtr()); + + createTarget(Triple, CPU, Features); + createFileManager(); + createSourceManager(); + + mInitialized = true; + + return; +} + +bool Compiler::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()) { + mDiagnostics->Report(clang::diag::err_fe_error_reading) << InputFile; + return false; + } + return true; +} + +bool Compiler::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()) { + mDiagnostics->Report(clang::diag::err_fe_error_reading) << InputFile; + return false; + } + + return true; +} + +bool Compiler::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, mDiagnostics.getPtr()); + break; + } + case OT_Nothing: { + break; + } + case OT_Object: + case OT_Bitcode: { + OS = openOutputFile(OutputFile, + llvm::raw_fd_ostream::F_Binary, + &Error, + mDiagnostics.getPtr()); + break; + } + default: { + llvm_unreachable("Unknown compiler output type"); + } + } + + if (!Error.empty()) + return false; + + mOS.reset(OS); + + mOutputFileName = OutputFile; + + return true; +} + +int Compiler::compile() { + if (mDiagnostics->hasErrorOccurred()) + return 1; + if (mOS.get() == NULL) + return 1; + + // Here is per-compilation needed initialization + createPreprocessor(); + createASTContext(); + + mBackend.reset(createBackend(mCodeGenOpts, &mOS->os(), mOT)); + + // Inform the diagnostic client we are processing a source file + mpDiagClient->BeginSourceFile(mLangOpts, mPP.get()); + + if (mLangOpts.CPlusPlus == 1) { + mPP->setPredefines("#define __cplusplus\n"); + } + + this->injectPreDefined(); + + // The core of the slang compiler + ParseAST(*mPP, mBackend.get(), *mASTContext); + + // Inform the diagnostic client we are done with previous source file + mpDiagClient->EndSourceFile(); + + // Declare success if no error + if (!mDiagnostics->hasErrorOccurred()) + mOS->keep(); + + // The compilation ended, clear + mBackend.reset(); + mASTContext.reset(); + mPP.reset(); + mOS.reset(); + + return mDiagnostics->hasErrorOccurred() ? 1 : 0; +} + +void Compiler::reset() { + mDiagnostics->Reset(); + return; +} + +Compiler::~Compiler() { + llvm::llvm_shutdown(); + return; +} + +} diff --git a/Compiler.h b/Compiler.h new file mode 100644 index 0000000..6209732 --- /dev/null +++ b/Compiler.h @@ -0,0 +1,161 @@ +#ifndef _LLVM_NDK_CC_COMPILER_H +#define _LLVM_NDK_CC_COMPILER_H + +#include <cstdio> +#include <map> +#include <string> +#include <vector> + +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/TargetOptions.h" + +#include "clang/Frontend/CodeGenOptions.h" + +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringRef.h" + +namespace llvm { + class tool_output_file; + class raw_ostream; +} + +namespace clang { + class Diagnostic; + class DiagnosticClient; + class FileManager; + class FileSystemOptions; + class SourceManager; + class Preprocessor; + class TargetOptions; + class ASTContext; + class ASTConsumer; + class Backend; + class TargetInfo; + class TextDiagnosticPrinter; +} + +namespace ndkpc { + +#define DEFAULT_TARGET_TRIPLE_STRING "armv7-none-linux-gnueabi" + +class Compiler { +public: + typedef enum { + OT_Dependency, + OT_Assembly, + OT_LLVMAssembly, + OT_Bitcode, + OT_Nothing, + OT_Object, + + OT_Default = OT_Bitcode + } OutputType; + + Compiler(); + ~Compiler(); + + void init(const std::string &Triple, const std::string &CPU, + const std::vector<std::string> &Features, + bool isCXX); + + bool setInputSource(llvm::StringRef InputFile, const char *Text, + size_t TextLength); + + bool setInputSource(llvm::StringRef InputFile); + + inline const std::string &getInputFileName() const { return mInputFileName; } + + inline void setIncludePaths(const std::vector<std::string> &IncludePaths) { + mIncludePaths = IncludePaths; + } + + inline void setPreDefinedSymbol(const std::map<std::string, std::string>& M) { + mPreDefinedSymbolMap = M; + } + + inline void setOutputType(OutputType OT) { mOT = OT; } + + bool setOutput(const char *OutputFile); + inline const std::string &getOutputFileName() const { + return mOutputFileName; + } + + int compile(); + + // Reset the slang compiler state such that it can be reused to compile + // another file + void reset(); + +private: + clang::LangOptions mLangOpts; + clang::CodeGenOptions mCodeGenOpts; + + static void LLVMErrorHandler(void *UserData, const std::string &Message); + + bool mInitialized; + + // The diagnostics engine instance (for status reporting during compilation) + llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> mDiagnostics; + // The diagnostics id + llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> mDiagIDs; + // The clients of diagnostics engine. The ownership is taken by the + // mDiagnostics after creation. + clang::TextDiagnosticPrinter *mpDiagClient; + void createDiagnostic(); + + // The target being compiled for + clang::TargetOptions mTargetOpts; + llvm::OwningPtr<clang::TargetInfo> mTarget; + void createTarget(const std::string &Triple, const std::string &CPU, + const std::vector<std::string> &Features); + + // Below is for parsing and code generation + + // The file manager (for prepocessor doing the job such as header file search) + llvm::OwningPtr<clang::FileManager> mFileMgr; + llvm::OwningPtr<clang::FileSystemOptions> mFileSysOpt; + void createFileManager(); + + // The source manager (responsible for the source code handling) + llvm::OwningPtr<clang::SourceManager> mSourceMgr; + void createSourceManager(); + + // The preprocessor (source code preprocessor) + llvm::OwningPtr<clang::Preprocessor> mPP; + void createPreprocessor(); + + // The AST context (the context to hold long-lived AST nodes) + llvm::OwningPtr<clang::ASTContext> mASTContext; + void createASTContext(); + + // The AST consumer, responsible for code generation + llvm::OwningPtr<clang::ASTConsumer> mBackend; + + // Input file name + std::string mInputFileName; + std::string mOutputFileName; + + OutputType mOT; + + // Output stream + llvm::OwningPtr<llvm::tool_output_file> mOS; + + std::vector<std::string> mIncludePaths; + + std::map<std::string, std::string> mPreDefinedSymbolMap; + void injectPreDefined(); + + void initDiagnostic() {} + void initPreprocessor() {} + void initASTContext() {} + + clang::ASTConsumer *createBackend(const clang::CodeGenOptions& CodeGenOpts, + llvm::raw_ostream *OS, + OutputType OT); +}; + +} + +#endif // _LLVM_NDK_CC_COMPILER_H diff --git a/llvm-ndk-cc.cpp b/llvm-ndk-cc.cpp new file mode 100644 index 0000000..b84b847 --- /dev/null +++ b/llvm-ndk-cc.cpp @@ -0,0 +1,102 @@ +#include <cassert> +#include <cstdlib> +#include <list> +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Option.h" +#include "clang/Driver/OptTable.h" + +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/OwningPtr.h" + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/system_error.h" + +#include "Compiler.h" + +// FIXME: Add parameter feature '-D macro=xxx' +static llvm::cl::opt<std::string> +InputFilename(llvm::cl::Positional, llvm::cl::Required, + llvm::cl::desc("<input file>")); + +static llvm::cl::list<std::string> +HeaderSearchDirs("I", llvm::cl::desc("Header search directory"), llvm::cl::Prefix); + +static llvm::cl::list<std::string> +PreDefinedSymbols("D", llvm::cl::desc("Pre-define symbol")); + +static llvm::cl::opt<std::string> +OutputFilename(llvm::cl::Required, "o", llvm::cl::desc("Override output filename")); + +// split "xxx" => "xxx" "" +// split "xxx=yyy" => "xxx" "yyy" +static void splitPreDefinedSymbol(const std::string& In, + std::string& Key, std::string& Value) { + size_t FoundPos = In.find("="); + if (FoundPos == std::string::npos) { + Key = In; + Value = ""; + } else { + Key = In.substr(0, FoundPos); + Value = In.substr(FoundPos+1, std::string::npos); + } +} + + +int main(int argc, char** argv) { + llvm::llvm_shutdown_obj _ShutdownObj; + llvm::cl::ParseCommandLineOptions(argc, argv, "P-NDK Compile Tool"); + + clang::TextDiagnosticPrinter* DiagClient = + new clang::TextDiagnosticPrinter(llvm::errs(), clang::DiagnosticOptions()); + DiagClient->setPrefix(argv[0]); + + llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> + DiagIDs(new clang::DiagnosticIDs()); + clang::DiagnosticsEngine Diags(DiagIDs, DiagClient, true); + + if (Diags.hasErrorOccurred()) + return 1; + + std::vector<std::string> IncludePaths; + for(unsigned i = 0, e = HeaderSearchDirs.size(); i<e; ++i) { + IncludePaths.push_back(HeaderSearchDirs[i]); + } + + std::map<std::string, std::string> PreDefinedSymbolMap; + for(unsigned i = 0, e = PreDefinedSymbols.size(); i<e; ++i) { + std::string Key; + std::string Value; + splitPreDefinedSymbol(PreDefinedSymbols[i], Key, Value); + PreDefinedSymbolMap.insert( + std::pair<std::string, std::string>(Key,Value)); + } + + ndkpc::Compiler Compiler; + Compiler.init(std::string(), + std::string(), + std::vector<std::string>(), + llvm::StringRef(InputFilename).endswith(".cpp")); + Compiler.setInputSource(InputFilename); + Compiler.setIncludePaths(IncludePaths); + Compiler.setOutput(OutputFilename.c_str()); + Compiler.setPreDefinedSymbol(PreDefinedSymbolMap); + + int ret = Compiler.compile(); + return ret; +} diff --git a/llvm-ndk-link.cpp b/llvm-ndk-link.cpp new file mode 100644 index 0000000..dc9eb8d --- /dev/null +++ b/llvm-ndk-link.cpp @@ -0,0 +1,136 @@ +#include <cassert> +#include <list> +#include <string> + +#include "llvm/Linker.h" +#include "llvm/LLVMContext.h" +#include "llvm/Module.h" +#include "llvm/PassManager.h" + +#include "llvm/ADT/OwningPtr.h" + +#include "llvm/Bitcode/ReaderWriter.h" + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Support/system_error.h" + +#include "llvm/Target/TargetData.h" + +static llvm::cl::list<std::string> +InputFilenames(llvm::cl::Positional, llvm::cl::OneOrMore, + llvm::cl::desc("<input bitcode files>")); + +static llvm::cl::list<std::string> +OutputFilenames("o", llvm::cl::desc("Override output filename"), + llvm::cl::value_desc("output bitcode file")); + + +static llvm::Module* getModuleFromFilename(std::string& Filename, + llvm::LLVMContext& Ctx, + std::string& ErrMsg) { + llvm::OwningPtr<llvm::MemoryBuffer> MB; + llvm::MemoryBuffer::getFile(Filename, MB); + llvm::Module* M = llvm::ParseBitcodeFile(MB.get(), Ctx, &ErrMsg); + assert(M && ErrMsg); + return M; +} + +static void optimizeModule(llvm::Module* M) { + llvm::PassManager Passes; + + const std::string &ModuleDataLayout = M->getDataLayout(); + if (!ModuleDataLayout.empty()) + if (llvm::TargetData *TD = new llvm::TargetData(ModuleDataLayout)) + Passes.add(TD); + + Passes.add(llvm::createInternalizePass(true/* AllButMain*/)); +#if 0 + FIXME REMOVE + createStandardLTOPasses(&Passes, + /* Internalize = */false, + /* RunInliner = */true, + /* VerifyEach = */false); +#endif + llvm::PassManagerBuilder PMBuilder; + PMBuilder.populateLTOPassManager(Passes, false, true); + Passes.run(*M); +} + +static llvm::Module* linkFilesToModule(llvm::cl::list<std::string>& Inputs, + llvm::LLVMContext& Ctx) { + std::string ErrMsg; + llvm::Module* M = getModuleFromFilename(Inputs[0], Ctx, ErrMsg); + llvm::Linker Linker("llvm-ndk-link", M); + + for (unsigned i=1; i<Inputs.size(); ++i) { + llvm::Module* M = getModuleFromFilename(Inputs[i], Ctx, ErrMsg); + if (!Linker.LinkInModule(M, &ErrMsg)) { + assert(false && ErrMsg); + } + optimizeModule(M); + } + M = Linker.releaseModule(); + + llvm::PassManager PM; + const std::string &ModuleDataLayout = M->getDataLayout(); + if (!ModuleDataLayout.empty()) + if (llvm::TargetData *TD = new llvm::TargetData(ModuleDataLayout)) + PM.add(TD); + +#if 0 + FIXME REMOVE + llvm::createStandardFunctionPasses(&PM, 3 /* OptLevel*/); + llvm::createStandardModulePasses(&PM, + 3, /* OptimizationLevel */ + true, /* OptimizeSize */ + true, /* UnitAtATime */ + true, /* UnrollLoops */ + true, /* SimplifyLibCalls */ + false, /* HaveExceptions */ + NULL /* InliningPass */); +#endif + + llvm::PassManagerBuilder PMBuilder; + //PMBuilder.OptLevel = 3; + //PMBuilder.populateFunctionPassManager(PM); + + PMBuilder.OptLevel = 3; + PMBuilder.SizeLevel = true; + PMBuilder.DisableUnitAtATime = false; + PMBuilder.DisableUnrollLoops = false; + PMBuilder.DisableSimplifyLibCalls = false; + PMBuilder.populateModulePassManager(PM); + + PM.run(*M); + return M; +} + +int main(int argc, char** argv) { + llvm::llvm_shutdown_obj _ShutdownObj; + llvm::cl::ParseCommandLineOptions(argc, argv, "P-NDK Link Tool"); + + llvm::LLVMContext& Ctx = llvm::getGlobalContext(); + std::string ErrMsg; + llvm::raw_fd_ostream FOS(OutputFilenames[0].c_str(), ErrMsg); + assert(!FOS.has_error()); + + // No need to link (just one file). + // Output Directly. + if (InputFilenames.size() == 1) { + llvm::OwningPtr<llvm::Module> M(getModuleFromFilename(InputFilenames[0], + Ctx, + ErrMsg)); + llvm::WriteBitcodeToFile(M.get(), FOS); + return 0; + } + + llvm::OwningPtr<llvm::Module> M(linkFilesToModule(InputFilenames, Ctx)); + llvm::WriteBitcodeToFile(M.get(), FOS); + assert(!FOS.has_error()); + return 0; +} diff --git a/tests/F_asm/asm.c b/tests/F_asm/asm.c new file mode 100644 index 0000000..88b450b --- /dev/null +++ b/tests/F_asm/asm.c @@ -0,0 +1,3 @@ +void root() { + __asm__("movl %eax, %ebx"); +} diff --git a/tests/F_asm/asm.stderr.txt.expect b/tests/F_asm/asm.stderr.txt.expect new file mode 100644 index 0000000..7709dde --- /dev/null +++ b/tests/F_asm/asm.stderr.txt.expect @@ -0,0 +1 @@ +Inline assembly is illigal. Please don't use it. diff --git a/tests/F_asm/asm.stdout.txt.expect b/tests/F_asm/asm.stdout.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/F_asm/asm.stdout.txt.expect diff --git a/tests/P_cxx/c_call.c b/tests/P_cxx/c_call.c new file mode 100644 index 0000000..981da43 --- /dev/null +++ b/tests/P_cxx/c_call.c @@ -0,0 +1,6 @@ +#include <elf.h> // for testing header search +#include "cxx.h" + +int main() { + c_interface(); +} diff --git a/tests/P_cxx/c_call.stderr.txt.expect b/tests/P_cxx/c_call.stderr.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_cxx/c_call.stderr.txt.expect diff --git a/tests/P_cxx/c_call.stdout.txt.expect b/tests/P_cxx/c_call.stdout.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_cxx/c_call.stdout.txt.expect diff --git a/tests/P_cxx/cxx.cpp b/tests/P_cxx/cxx.cpp new file mode 100644 index 0000000..197e91b --- /dev/null +++ b/tests/P_cxx/cxx.cpp @@ -0,0 +1,21 @@ +#include "cxx.h" + +void foo() {} +void foo(int a) {} +int bar; + +namespace { + void foo() {} + void foo(double a) {} + int bar; +} + +namespace kerker { + void foo() {} + void foo(char* a) {} + int bar; +} + +extern "C" void c_interface() { + kerker::foo(); +} diff --git a/tests/P_cxx/cxx.h b/tests/P_cxx/cxx.h new file mode 100644 index 0000000..2c0a972 --- /dev/null +++ b/tests/P_cxx/cxx.h @@ -0,0 +1,9 @@ +#ifdef __cplusplus +extern "C" { +#endif + +void c_interface(); + +#ifdef __cplusplus +} +#endif diff --git a/tests/P_cxx/cxx.stderr.txt.expect b/tests/P_cxx/cxx.stderr.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_cxx/cxx.stderr.txt.expect diff --git a/tests/P_cxx/cxx.stdout.txt.expect b/tests/P_cxx/cxx.stdout.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_cxx/cxx.stdout.txt.expect diff --git a/tests/P_define/def.c b/tests/P_define/def.c new file mode 100644 index 0000000..54a2ece --- /dev/null +++ b/tests/P_define/def.c @@ -0,0 +1,7 @@ +#ifndef kerker +#define kerker 5566 +#endif + +int main() { + int i = kerker; +} diff --git a/tests/P_define/def.stderr.txt.expect b/tests/P_define/def.stderr.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_define/def.stderr.txt.expect diff --git a/tests/P_define/def.stdout.txt.expect b/tests/P_define/def.stdout.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_define/def.stdout.txt.expect diff --git a/tests/P_hello/bar.c b/tests/P_hello/bar.c new file mode 100644 index 0000000..9cd2ef3 --- /dev/null +++ b/tests/P_hello/bar.c @@ -0,0 +1,7 @@ +int bar() { + int result = 0; + for (int i = 0; i<5566; ++i) { + result += 1; + } + return result; +} diff --git a/tests/P_hello/bar.stderr.txt.expect b/tests/P_hello/bar.stderr.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_hello/bar.stderr.txt.expect diff --git a/tests/P_hello/bar.stdout.txt.expect b/tests/P_hello/bar.stdout.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_hello/bar.stdout.txt.expect diff --git a/tests/P_hello/hello-llvm.c b/tests/P_hello/hello-llvm.c new file mode 100644 index 0000000..886afb8 --- /dev/null +++ b/tests/P_hello/hello-llvm.c @@ -0,0 +1,6 @@ +int bar(); + +double hello() { + int foobar = bar() + 123; + return (double)foobar; +} diff --git a/tests/P_hello/hello-llvm.stderr.txt.expect b/tests/P_hello/hello-llvm.stderr.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_hello/hello-llvm.stderr.txt.expect diff --git a/tests/P_hello/hello-llvm.stdout.txt.expect b/tests/P_hello/hello-llvm.stdout.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_hello/hello-llvm.stdout.txt.expect diff --git a/tests/P_null_file/null.c b/tests/P_null_file/null.c new file mode 100644 index 0000000..ac12275 --- /dev/null +++ b/tests/P_null_file/null.c @@ -0,0 +1,2 @@ +int i; +int j; diff --git a/tests/P_null_file/null.stderr.txt.expect b/tests/P_null_file/null.stderr.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_null_file/null.stderr.txt.expect diff --git a/tests/P_null_file/null.stdout.txt.expect b/tests/P_null_file/null.stdout.txt.expect new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/P_null_file/null.stdout.txt.expect diff --git a/tests/test.py b/tests/test.py new file mode 100755 index 0000000..40f5878 --- /dev/null +++ b/tests/test.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- +"""llvm-ndk-cc Toolchains Test. +""" + +import filecmp +import glob +import os +import subprocess +import sys + +__author__ = 'Nowar Gu' + +class Options(object): + Verbose = 0 + Cleanup = 1 + +def compareFile(Filename): + """Compare Filename and Filename.expect for equality.""" + Actual = Filename + Expect = Filename + '.expect' + + if not os.path.isfile(Actual): + if Options.Verbose: + print('Could not find {0}'.format(Actual)) + return False + if not os.path.isfile(Expect): + if Options.Verbose: + print('Could not find {0}'.format(Expect)) + return False + + return filecmp.cmp(Actual, Expect, False) + + +def runFileTest(Filename): + """Run test on each file in directory.""" + Passed = True + + if Options.Verbose != 0: + print('\nTesting {0}'.format(Filename)) + + Cmd = ('../../../../../out/host/linux-x86/bin/llvm-ndk-cc' + ' -I../../../../platforms/android-100/arch-llvm/usr/include' + ' -I{0}'.format(os.path.dirname(os.path.realpath(Filename)))) + + BaseArgs = Cmd.split() + FilenameStubs = Filename.split('.') + FileBasename = FilenameStubs[0] + Args = BaseArgs + Args.append(Filename) + Args.append('-o') + Args.append(FileBasename + '.bc') + + StdoutFile = open('{0}.stdout.txt'.format(FileBasename), 'w+') + StderrFile = open('{0}.stderr.txt'.format(FileBasename), 'w+') + + if Options.Verbose > 1: + print('Executing:',end=' ') + for Arg in Args: + print(Arg,end=' ') + print() + Ret = subprocess.call(Args, stdout=StdoutFile, stderr=StderrFile) + StdoutFile.flush() + StderrFile.flush() + if Options.Verbose > 1: + StdoutFile.seek(0) + StderrFile.seek(0) + for Line in StdoutFile: + print('STDOUT> {0}'.format(Line), end='') + for Line in StderrFile: + print('STDERR> {0}'.format(Line), end='') + + StdoutFile.close() + StderrFile.close() + return Ret == 0 + + +def runTest(Dirname): + """Run an llvm-ndk-cc test from Dirname.""" + Passed = True + os.chdir(Dirname) + + SrcFiles = glob.glob('*.c') + SrcFiles.extend(glob.glob('*.cpp')) + SrcFiles.sort() + FilesPassed = [] + ResultPassed = True + for File in SrcFiles: + FileRunPassed = runFileTest(File) + FileBasename = File.split('.')[0] + if not compareFile('{0}.stdout.txt'.format(FileBasename)): + ResultPassed = False + if Options.Verbose: + print('stdout is different.') + if not compareFile('{0}.stderr.txt'.format(FileBasename)): + ResultPassed = False + if Options.Verbose: + print('stderr is different.') + FilesPassed.append(FileRunPassed) + + Result = True + for FilePassed in FilesPassed: + Result = Result and FilePassed + + if Dirname[0:2] == 'F_': + if Result == True: + Result = False + if Options.Verbose: + print('Command passed on invalid input.') + else: + Result = True + elif Dirname[0:2] == 'P_': + if Result == False: + if Options.Verbose: + print('Command failed on valid input.') + else: + if Options.Verbose: + print('Test directory name should start with an F or a P.') + + Cmd = ('../../../../../out/host/linux-x86/bin/llvm-ndk-link') + Args = Cmd.split() + Args.extend(glob.glob('*.bc')) + Args.append('-o') + Args.append('AllFilesLinked.bc') + if Result == True: + Ret = subprocess.call(Args) + if Ret != 0: + Result = False; + + if Options.Verbose > 1: + print() + print('Executing:',end=' ') + for Arg in Args: + print(Arg,end=' ') + print() + + + if Options.Cleanup: + for File in glob.glob('*.stdout.txt'): + os.remove(File) + for File in glob.glob('*.stderr.txt'): + os.remove(File) + for File in glob.glob('*.bc'): + os.remove(File) + + os.chdir('..') + if Dirname[0:2] == 'F_': + return not Result and ResultPassed + else: + return Result and ResultPassed + + +def showUsage(): + print('Usage: {0} [Option]... [directory]...'.format(sys.argv[0])) + print('llvm-ndk-cc Toolchains Test') + print(' -h, --help Help message') + print(' -n, --no-cleanup Do not cleanup after testing') + print(' -v, --verbose Verbose output') + return + + +def main(): + Passed = 0 + Failed = 0 + Files = [] + FailedTests = [] + + for Arg in sys.argv[1:]: + if Arg in ['-h', '--help']: + showUsage() + return 0 + elif Arg in ['-n', '--no-cleanup']: + Options.Cleanup = 0 + elif Arg in ['-v', '--verbose']: + Options.Verbose += 1 + else: + if os.path.isdir(Arg): + Files.append(Arg) + else: + print('Invalid test or options: {0}'.format(Arg), file=sys.stderr) + return 1 + + if not Files: + TmpFiles = os.listdir('.') + for File in TmpFiles: + if os.path.isdir(File) and (File[0:2] == 'F_' or File[0:2] == 'P_'): + Files.append(File) + + for Dir in Files: + if os.path.isdir(Dir): + if runTest(Dir): + Passed += 1 + else: + Failed += 1 + FailedTests.append(Dir) + + print() + print('Tests Passed: {0}'.format(Passed)) + print('Tests Failed: {0}'.format(Failed)) + if Failed: + print('Failures:', end=' ') + for Test in FailedTests: + print(Test, end=' ') + print() + + return Failed != 0 + + +if __name__ == '__main__': + sys.exit(main()) |