summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Hsieh <andrewhsieh@google.com>2012-07-31 15:54:16 -0700
committerAndrew Hsieh <andrewhsieh@google.com>2012-07-31 15:54:16 -0700
commit42552ec6168d994991696a04c1626ac91218a720 (patch)
tree33fb886ae12c33dc1d0d32130a2c7d0b60572fed
parent8ad720504e84a65fc3d01947a2effed67ad47346 (diff)
downloadllvm-ndk-cc-42552ec6168d994991696a04c1626ac91218a720.tar.gz
llvm-ndk-cc: driver of NDK llvm ABI
Change-Id: I6b230d089fd7f3b944504b0a1f9a980cc3524aec
-rw-r--r--Android.mk145
-rw-r--r--Backend.cpp334
-rw-r--r--Backend.h100
-rw-r--r--Compiler.cpp333
-rw-r--r--Compiler.h161
-rw-r--r--llvm-ndk-cc.cpp102
-rw-r--r--llvm-ndk-link.cpp136
-rw-r--r--tests/F_asm/asm.c3
-rw-r--r--tests/F_asm/asm.stderr.txt.expect1
-rw-r--r--tests/F_asm/asm.stdout.txt.expect0
-rw-r--r--tests/P_cxx/c_call.c6
-rw-r--r--tests/P_cxx/c_call.stderr.txt.expect0
-rw-r--r--tests/P_cxx/c_call.stdout.txt.expect0
-rw-r--r--tests/P_cxx/cxx.cpp21
-rw-r--r--tests/P_cxx/cxx.h9
-rw-r--r--tests/P_cxx/cxx.stderr.txt.expect0
-rw-r--r--tests/P_cxx/cxx.stdout.txt.expect0
-rw-r--r--tests/P_define/def.c7
-rw-r--r--tests/P_define/def.stderr.txt.expect0
-rw-r--r--tests/P_define/def.stdout.txt.expect0
-rw-r--r--tests/P_hello/bar.c7
-rw-r--r--tests/P_hello/bar.stderr.txt.expect0
-rw-r--r--tests/P_hello/bar.stdout.txt.expect0
-rw-r--r--tests/P_hello/hello-llvm.c6
-rw-r--r--tests/P_hello/hello-llvm.stderr.txt.expect0
-rw-r--r--tests/P_hello/hello-llvm.stdout.txt.expect0
-rw-r--r--tests/P_null_file/null.c2
-rw-r--r--tests/P_null_file/null.stderr.txt.expect0
-rw-r--r--tests/P_null_file/null.stdout.txt.expect0
-rwxr-xr-xtests/test.py210
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())