Index: examples/clang-interpreter/CMakeLists.txt =================================================================== --- examples/clang-interpreter/CMakeLists.txt +++ examples/clang-interpreter/CMakeLists.txt @@ -3,13 +3,17 @@ ExecutionEngine MC MCJIT + OrcJit Option + RuntimeDyld Support native ) add_clang_executable(clang-interpreter main.cpp + Invoke.cpp + Manager.cpp ) add_dependencies(clang-interpreter @@ -23,3 +27,67 @@ clangDriver clangFrontend ) + +export_executable_symbols(clang-interpreter) + +if (MSVC) + # Is this a CMake bug that even with export_executable_symbols, Windows + # needs to explictly export the type_info vtable + set_property(TARGET clang-interpreter + APPEND_STRING PROPERTY LINK_FLAGS /EXPORT:??_7type_info@@6B@) +endif() + +function(clang_enable_exceptions TARGET) + # Really have to jump through hoops to enable exception handling independent + # of how LLVM is being built. + if (NOT LLVM_REQUIRES_EH AND NOT LLVM_REQUIRES_RTTI) + if (MSVC) + # /EHs to allow throwing rom extern "C" + set(excptnExceptions_ON "/D _HAS_EXCEPTIONS=1 /EHs /wd4714") + set(excptnExceptions_OFF "/D _HAS_EXCEPTIONS=0 /EHs-c-") + set(excptnRTTI_ON "/GR") + set(excptnRTTI_OFF "/GR-") + set(excptnEHRTTIRegEx "(/EHs(-c-?)|_HAS_EXCEPTIONS=(0|1))") + else() + set(excptnExceptions_ON "-fexceptions") + set(excptnExceptions_OFF "-fno-exceptions") + set(excptnRTTI_ON "-frtti") + set(excptnRTTI_OFF "-fno-rtti") + set(excptnEHRTTIRegEx "-f(exceptions|no-exceptions)") + endif() + if (LLVM_REQUIRES_EH) + set(excptnExceptions_DFLT ${excptnExceptions_ON}) + else() + set(excptnExceptions_DFLT ${excptnExceptions_OFF}) + endif() + if (LLVM_REQUIRES_RTTI) + set(excptnRTTI_DFLT ${excptnRTTI_ON}) + else() + set(excptnRTTI_DFLT ${excptnRTTI_OFF}) + endif() + + # Strip the exception & rtti flags from the target + get_property(addedFlags TARGET ${TARGET} PROPERTY COMPILE_FLAGS) + string(REGEX REPLACE ${excptnEHRTTIRegEx} "" editedFlags ${addedFlags}) + string(REPLACE ${excptnRTTI_OFF} "" editedFlags ${editedFlags}) + set_property(TARGET ${TARGET} PROPERTY COMPILE_FLAGS ${editedFlags}) + + get_property(addedFlags TARGET ${TARGET} PROPERTY COMPILE_DEFINITIONS) + string(REGEX REPLACE ${excptnEHRTTIRegEx} "" editedFlags ${addedFlags}) + string(REPLACE ${excptnRTTI_OFF} "" editedFlags ${editedFlags}) + set_property(TARGET ${TARGET} PROPERTY COMPILE_DEFINITIONS ${editedFlags}) + + # Re-add the exception & rtti flags from LLVM + set_property(SOURCE main.cpp APPEND_STRING PROPERTY COMPILE_FLAGS + " ${excptnExceptions_DFLT} ${excptnRTTI_DFLT} ") + set_property(SOURCE Manager.cpp APPEND_STRING PROPERTY COMPILE_FLAGS + " ${excptnExceptions_DFLT} ${excptnRTTI_DFLT} ") + + # Invoke with exceptions & rtti + set_property(SOURCE Invoke.cpp APPEND_STRING PROPERTY COMPILE_FLAGS + " ${excptnExceptions_ON} ${excptnRTTI_ON} ") + + endif() +endfunction(clang_enable_exceptions) + +clang_enable_exceptions(clang-interpreter) Index: examples/clang-interpreter/Invoke.h =================================================================== --- examples/clang-interpreter/Invoke.h +++ examples/clang-interpreter/Invoke.h @@ -0,0 +1,34 @@ +//===-- examples/clang-interpreter/Invoke.h - Clang C Interpreter Example -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_EXAMPLE_INTERPRETER_INVOKE_H +#define CLANG_EXAMPLE_INTERPRETER_INVOKE_H + +namespace llvm { + class ExecutionEngine; + class Function; +} + +#include +#include + +namespace interpreter { + +typedef std::vector InvokeArgs; + +typedef int (*Invoker)(llvm::ExecutionEngine *EE, llvm::Function *EntryFn, + const InvokeArgs &Args, char *const *EnvP); + +int TryIt(llvm::ExecutionEngine *EE, llvm::Function *EntryFn, + const InvokeArgs &Args, char *const *EnvP, + Invoker Invoke); + +} // interpreter + +#endif // CLANG_EXAMPLE_INTERPRETER_INVOKE_H Index: examples/clang-interpreter/Invoke.cpp =================================================================== --- examples/clang-interpreter/Invoke.cpp +++ examples/clang-interpreter/Invoke.cpp @@ -0,0 +1,31 @@ +//==-- examples/clang-interpreter/Invoke.cpp - Clang C Interpreter Example -==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Invoke.h" + +#include +#include + +namespace interpreter { + +int TryIt(llvm::ExecutionEngine *EE, llvm::Function *EntryFn, + const std::vector &Args, char *const *EnvP, + Invoker Invoke) { + int Res = -1; + try { + Res = Invoke(EE, EntryFn, Args, EnvP); + } catch (const std::exception &E) { + std::cout << "Caught '" << E.what() << "'\n"; + } catch (...) { + std::cout << "Unknown exception\n"; + } + return Res; +} + +} Index: examples/clang-interpreter/Manager.h =================================================================== --- examples/clang-interpreter/Manager.h +++ examples/clang-interpreter/Manager.h @@ -0,0 +1,59 @@ +//===-- examples/clang-interpreter/Manager.h - Clang C Interpreter Example -==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_EXAMPLE_INTERPRETER_MANAGER_H +#define CLANG_EXAMPLE_INTERPRETER_MANAGER_H + +#include "llvm/ExecutionEngine/SectionMemoryManager.h" + +#if defined(LLVM_ON_WIN32) && defined(_WIN64) +#define CLANG_INTERPRETER_COFF_FORMAT +#define CLANG_INTERPRETER_WIN_EXCEPTIONS +#endif + +namespace interpreter { + +class SingleSectionMemoryManager : public llvm::SectionMemoryManager { + struct Block { + uint8_t *Addr = nullptr, *End = nullptr; + void Reset(uint8_t *Ptr, uintptr_t Size); + uint8_t *Next(uintptr_t Size, unsigned Alignment); + }; + Block Code, ROData, RWData; + +public: + uint8_t *allocateCodeSection(uintptr_t Size, unsigned Align, unsigned ID, + llvm::StringRef Name) final; + + uint8_t *allocateDataSection(uintptr_t Size, unsigned Align, unsigned ID, + llvm::StringRef Name, bool RO) final; + + void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign, + uintptr_t ROSize, uint32_t ROAlign, + uintptr_t RWSize, uint32_t RWAlign) final; + + bool needsToReserveAllocationSpace() override { return true; } + +#ifdef CLANG_INTERPRETER_WIN_EXCEPTIONS + using llvm::SectionMemoryManager::EHFrameInfos; + + SingleSectionMemoryManager(); + + void deregisterEHFrames() override; + + bool finalizeMemory(std::string *ErrMsg) override; + +private: + uintptr_t ImageBase = 0; +#endif +}; + +} + +#endif // CLANG_EXAMPLE_INTERPRETER_MANAGER_H Index: examples/clang-interpreter/Manager.cpp =================================================================== --- examples/clang-interpreter/Manager.cpp +++ examples/clang-interpreter/Manager.cpp @@ -0,0 +1,328 @@ +//==-- examples/clang-interpreter/Manager.cpp - Clang C Interpreter Example -=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Manager.h" + +#ifdef CLANG_INTERPRETER_WIN_EXCEPTIONS +#include "llvm/Support/DynamicLibrary.h" + +#define WIN32_LEAN_AND_MEAN +#define NOGDI +#define NOMINMAX +#include +#endif + +namespace interpreter { + +using namespace llvm; + +void SingleSectionMemoryManager::Block::Reset(uint8_t *Ptr, uintptr_t Size) { + assert(Ptr != nullptr && "Bad allocation"); + Addr = Ptr; + End = Ptr ? Ptr + Size : nullptr; +} + +uint8_t *SingleSectionMemoryManager::Block::Next(uintptr_t Size, + unsigned Alignment) { + uintptr_t Out = (uintptr_t)Addr; + + // Align the out pointer properly + if (!Alignment) + Alignment = 16; + Out = (Out + Alignment - 1) & ~(uintptr_t)(Alignment - 1); + + // RuntimeDyld should have called reserveAllocationSpace with an amount that + // will fit all required alignemnts...but assert on this to make sure. + assert((Out + Size) <= (uintptr_t)End && "Out of bounds"); + + // Set the next Addr to deliver at the end of this one. + Addr = (uint8_t *)(Out + Size); + return (uint8_t *)Out; +} + +uint8_t *SingleSectionMemoryManager::allocateCodeSection(uintptr_t Size, + unsigned Align, + unsigned ID, + StringRef Name) { + return Code.Next(Size, Align); +} + +uint8_t *SingleSectionMemoryManager::allocateDataSection( + uintptr_t Size, unsigned Align, unsigned ID, StringRef Name, bool RO) { + return RO ? ROData.Next(Size, Align) : RWData.Next(Size, Align); +} + +void SingleSectionMemoryManager::reserveAllocationSpace( + uintptr_t CodeSize, uint32_t CodeAlign, uintptr_t ROSize, uint32_t ROAlign, + uintptr_t RWSize, uint32_t RWAlign) { + // FIXME: Ideally this should be one contiguous block, with Code, ROData, + // and RWData pointing to sub-blocks within, but setting the correct + // permissions for that wouldn't work unless we over-allocated to have each + // Block.Base aligned on a page boundary. + const unsigned SecID = 0; + Code.Reset(SectionMemoryManager::allocateCodeSection(CodeSize, CodeAlign, + SecID, "code"), + CodeSize); + + ROData.Reset(SectionMemoryManager::allocateDataSection(ROSize, ROAlign, SecID, + "rodata", true/*RO*/), + ROSize); + + RWData.Reset(SectionMemoryManager::allocateDataSection(RWSize, RWAlign, SecID, + "rwdata", false/*RO*/), + RWSize); + +#ifdef CLANG_INTERPRETER_WIN_EXCEPTIONS + ImageBase = + (uintptr_t)std::min(std::min(Code.Addr, ROData.Addr), RWData.Addr); +#endif +} + +#ifdef CLANG_INTERPRETER_WIN_EXCEPTIONS + +// Map an "ImageBase" to a range of adresses that can throw. +// +class SEHFrameHandler { + typedef SingleSectionMemoryManager::EHFrameInfos EHFrameInfos; + typedef std::vector> ImageRanges; + typedef std::map ImageBaseMap; + ImageBaseMap m_Map; + + static void MergeRanges(ImageRanges &Ranges); + uintptr_t FindEHFrame(uintptr_t Caller); + +public: + static __declspec(noreturn) void __stdcall RaiseSEHException(void *, void *); + void RegisterEHFrames(uintptr_t ImageBase, const EHFrameInfos &Frames, + bool Block = true); + void DeRegisterEHFrames(uintptr_t ImageBase, const EHFrameInfos &Frames); +}; + +// Merge overlaping ranges for faster searching with throwing PC +void SEHFrameHandler::MergeRanges(ImageRanges &Ranges) { + std::sort(Ranges.begin(), Ranges.end()); + + ImageRanges Merged; + ImageRanges::iterator It = Ranges.begin(); + auto Current = *(It)++; + while (It != Ranges.end()) { + if (Current.second + 1 < It->first) { + Merged.push_back(Current); + Current = *(It); + } else + Current.second = std::max(Current.second, It->second); + ++It; + } + Merged.emplace_back(Current); + Ranges.swap(Merged); +} + +// Find the "ImageBase" for Caller/PC who is throwing an exception +uintptr_t SEHFrameHandler::FindEHFrame(uintptr_t Caller) { + for (auto &&Itr : m_Map) { + const uintptr_t ImgBase = Itr.first; + for (auto &&Rng : Itr.second) { + if (Caller >= (ImgBase + Rng.first) && Caller <= (ImgBase + Rng.second)) + return ImgBase; + } + } + return 0; +} + +// Register a range of adresses for a single section that +void SEHFrameHandler::RegisterEHFrames(uintptr_t ImageBase, + const EHFrameInfos &Frames, bool Block) { + if (Frames.empty()) + return; + assert(m_Map.find(ImageBase) == m_Map.end()); + + ImageBaseMap::mapped_type &Ranges = m_Map[ImageBase]; + ImageRanges::value_type *BlockRange = nullptr; + if (Block) { + // Merge all unwind adresses into a single contiguous block for faster + // searching later. + Ranges.emplace_back(std::numeric_limits::max(), + std::numeric_limits::min()); + BlockRange = &Ranges.back(); + } + + for (auto &&Frame : Frames) { + assert(m_Map.find(DWORD64(Frame.Addr)) == m_Map.end() && + "Runtime function should not be a key!"); + + PRUNTIME_FUNCTION RFunc = reinterpret_cast(Frame.Addr); + const size_t N = Frame.Size / sizeof(RUNTIME_FUNCTION); + if (BlockRange) { + for (PRUNTIME_FUNCTION It = RFunc, End = RFunc + N; It < End; ++It) { + BlockRange->first = std::min(BlockRange->first, It->BeginAddress); + BlockRange->second = std::max(BlockRange->second, It->EndAddress); + } + } else { + for (PRUNTIME_FUNCTION It = RFunc, End = RFunc + N; It < End; ++It) + Ranges.emplace_back(It->BeginAddress, It->EndAddress); + } + + ::RtlAddFunctionTable(RFunc, N, ImageBase); + } + + if (!Block) + MergeRanges(Ranges); // Initial sort and merge +} + +void SEHFrameHandler::DeRegisterEHFrames(uintptr_t ImageBase, + const EHFrameInfos &Frames) { + if (Frames.empty()) + return; + + auto Itr = m_Map.find(ImageBase); + if (Itr != m_Map.end()) { + // Remove the ImageBase from lookup + m_Map.erase(Itr); + + // Unregister all the PRUNTIME_FUNCTIONs + for (auto &&Frame : Frames) + ::RtlDeleteFunctionTable(reinterpret_cast(Frame.Addr)); + } +} + +// FIXME: Rather than this static and overriding _CxxThrowException via +// DynamicLibrary::AddSymbol, a better route would be to transform the call +// to _CxxThrowException(Arg0, Arg1) -> RaiseSEHException(Arg0, Arg1, this) +// where 'this' is the SingleSectionMemoryManager instance. This could probably +// be done with clang, and definitely possible by injecting an llvm-IR function +// into the module with the name '_CxxThrowException' +// +static SEHFrameHandler sFrameHandler; + +void SingleSectionMemoryManager::deregisterEHFrames() { + sFrameHandler.DeRegisterEHFrames(ImageBase, EHFrames); + EHFrameInfos().swap(EHFrames); +} + +bool SingleSectionMemoryManager::finalizeMemory(std::string *ErrMsg) { + sFrameHandler.RegisterEHFrames(ImageBase, EHFrames); + ImageBase = 0; + return SectionMemoryManager::finalizeMemory(ErrMsg); +} + +SingleSectionMemoryManager::SingleSectionMemoryManager() { + // Override Windows _CxxThrowException to call into our local version that + // can throw to and from the JIT. + sys::DynamicLibrary::AddSymbol( + "_CxxThrowException", + (void *)(uintptr_t)&SEHFrameHandler::RaiseSEHException); +} + +// Adapted from VisualStudio/VC/crt/src/vcruntime/throw.cpp +#ifdef _WIN64 +#define _EH_RELATIVE_OFFSETS 1 +#endif +// The NT Exception # that we use +#define EH_EXCEPTION_NUMBER ('msc' | 0xE0000000) +// The magic # identifying this version +#define EH_MAGIC_NUMBER1 0x19930520 +#define EH_PURE_MAGIC_NUMBER1 0x01994000 +// Number of parameters in exception record +#define EH_EXCEPTION_PARAMETERS 4 + +// A generic exception record +struct EHExceptionRecord { + DWORD ExceptionCode; + DWORD ExceptionFlags; // Flags determined by NT + _EXCEPTION_RECORD *ExceptionRecord; // Extra exception record (unused) + void *ExceptionAddress; // Address at which exception occurred + DWORD NumberParameters; // No. of parameters = EH_EXCEPTION_PARAMETERS + struct EHParameters { + DWORD magicNumber; // = EH_MAGIC_NUMBER1 + void *pExceptionObject; // Pointer to the actual object thrown + struct ThrowInfo *pThrowInfo; // Description of thrown object +#if _EH_RELATIVE_OFFSETS + DWORD64 pThrowImageBase; // Image base of thrown object +#endif + } params; +}; + +__declspec(noreturn) void __stdcall +SEHFrameHandler::RaiseSEHException(void *CxxExcept, void *Info) { + uintptr_t Caller; + static_assert(sizeof(Caller) == sizeof(PVOID), "Size mismatch"); + + USHORT Frames = CaptureStackBackTrace(1, 1, (PVOID *)&Caller, NULL); + assert(Frames && "No frames captured"); + (void)Frames; + + const DWORD64 BaseAddr = sFrameHandler.FindEHFrame(Caller); + if (BaseAddr == 0) + _CxxThrowException(CxxExcept, (_ThrowInfo *)Info); + + // A generic exception record + EHExceptionRecord Exception = { + EH_EXCEPTION_NUMBER, // Exception number + EXCEPTION_NONCONTINUABLE, // Exception flags (we don't do resume) + nullptr, // Additional record (none) + nullptr, // Address of exception (OS fills in) + EH_EXCEPTION_PARAMETERS, // Number of parameters + {EH_MAGIC_NUMBER1, CxxExcept, (struct ThrowInfo *)Info, +#if _EH_RELATIVE_OFFSETS + BaseAddr +#endif + }}; + +// const ThrowInfo* pTI = (const ThrowInfo*)Info; + +#ifdef THROW_ISWINRT + if (pTI && (THROW_ISWINRT((*pTI)))) { + // The pointer to the ExceptionInfo structure is stored sizeof(void*) + // infront of each WinRT Exception Info. + ULONG_PTR *EPtr = *reinterpret_cast(CxxExcept); + EPtr--; + + WINRTEXCEPTIONINFO **ppWei = reinterpret_cast(EPtr); + pTI = (*ppWei)->throwInfo; + (*ppWei)->PrepareThrow(ppWei); + } +#endif + + // If the throw info indicates this throw is from a pure region, + // set the magic number to the Pure one, so only a pure-region + // catch will see it. + // + // Also use the Pure magic number on Win64 if we were unable to + // determine an image base, since that was the old way to determine + // a pure throw, before the TI_IsPure bit was added to the FuncInfo + // attributes field. + if (Info != nullptr) { +#ifdef THROW_ISPURE + if (THROW_ISPURE(*pTI)) + Exception.params.magicNumber = EH_PURE_MAGIC_NUMBER1; +#if _EH_RELATIVE_OFFSETS + else +#endif // _EH_RELATIVE_OFFSETS +#endif // THROW_ISPURE + + // Not quite sure what this is about, but pThrowImageBase can never be 0 + // here, as that is used to mark when an "ImageBase" was not found. +#if 0 && _EH_RELATIVE_OFFSETS + if (Exception.params.pThrowImageBase == 0) + Exception.params.magicNumber = EH_PURE_MAGIC_NUMBER1; +#endif // _EH_RELATIVE_OFFSETS + } + +// Hand it off to the OS: +#if defined(_M_X64) && defined(_NTSUBSET_) + RtlRaiseException((PEXCEPTION_RECORD)&Exception); +#else + RaiseException(Exception.ExceptionCode, Exception.ExceptionFlags, + Exception.NumberParameters, (PULONG_PTR)&Exception.params); +#endif +} + +#endif // CLANG_INTERPRETER_WIN_EXCEPTIONS + +} // namespace interpreter Index: examples/clang-interpreter/README.txt =================================================================== --- examples/clang-interpreter/README.txt +++ examples/clang-interpreter/README.txt @@ -1,4 +1,4 @@ -This is an example of Clang based interpreter, for executing standalone C +This is an example of Clang based interpreter, for executing standalone C/C++ programs. It demonstrates the following features: @@ -12,6 +12,9 @@ 4. Use the LLVM JIT functionality to execute the final module. + 5. Intercepting a Win64 library call to allow throwing and catching exceptions + in and from the JIT. + The implementation has many limitations and is not designed to be a full fledged -C interpreter. It is designed to demonstrate a simple but functional use of the +interpreter. It is designed to demonstrate a simple but functional use of the Clang compiler libraries. Index: examples/clang-interpreter/Test.cxx =================================================================== --- examples/clang-interpreter/Test.cxx +++ examples/clang-interpreter/Test.cxx @@ -0,0 +1,34 @@ +//===-- examples/clang-interpreter/Test.cxx - Clang C Interpreter Example -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Example throwing in and from the JIT (particularly on Win64). +// +// ./bin/clang-interpreter /tools/clang/examples/clang-interpreter/Test.cxx + +#include +#include + +static void ThrowerAnError(const char* Name) { + throw std::runtime_error(Name); +} + +int main(int argc, const char** argv) { + for (int I = 0; I < argc; ++I) + printf("arg[%d]='%s'\n", I, argv[I]); + + try { + ThrowerAnError("In JIT"); + } catch (const std::exception& E) { + printf("Caught: '%s'\n", E.what()); + } catch (...) { + printf("Unknown exception\n"); + } + ThrowerAnError("From JIT"); + return 0; +} Index: examples/clang-interpreter/main.cpp =================================================================== --- examples/clang-interpreter/main.cpp +++ examples/clang-interpreter/main.cpp @@ -7,6 +7,9 @@ // //===----------------------------------------------------------------------===// +#include "Invoke.h" +#include "Manager.h" + #include "clang/CodeGen/CodeGenAction.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Driver/Compilation.h" @@ -26,60 +29,45 @@ #include "llvm/Support/Path.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" -#include + using namespace clang; using namespace clang::driver; +namespace interpreter { + +static llvm::ExecutionEngine * +createExecutionEngine(std::unique_ptr M, std::string *ErrorStr) { + llvm::EngineBuilder EB(std::move(M)); + EB.setErrorStr(ErrorStr); + EB.setMemoryManager(llvm::make_unique()); + llvm::ExecutionEngine *EE = EB.create(); + EE->finalizeObject(); + return EE; +} + +// Invoked from a try/catch block in invoke.cpp. +// +static int Invoke(llvm::ExecutionEngine *EE, llvm::Function *EntryFn, + const std::vector &Args, char *const *EnvP) { + return EE->runFunctionAsMain(EntryFn, Args, EnvP); +} + // This function isn't referenced outside its translation unit, but it // can't use the "static" keyword because its address is used for // GetMainExecutable (since some platforms don't support taking the // address of main, and some platforms can't implement GetMainExecutable // without being given the address of a function in the main executable). -std::string GetExecutablePath(const char *Argv0) { - // This just needs to be some symbol in the binary; C++ doesn't - // allow taking the address of ::main however. - void *MainAddr = (void*) (intptr_t) GetExecutablePath; +std::string GetExecutablePath(const char *Argv0, void *MainAddr) { return llvm::sys::fs::getMainExecutable(Argv0, MainAddr); } -static llvm::ExecutionEngine * -createExecutionEngine(std::unique_ptr M, std::string *ErrorStr) { - return llvm::EngineBuilder(std::move(M)) - .setEngineKind(llvm::EngineKind::Either) - .setErrorStr(ErrorStr) - .create(); -} - -static int Execute(std::unique_ptr Mod, char *const *envp) { - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - - llvm::Module &M = *Mod; - std::string Error; - std::unique_ptr EE( - createExecutionEngine(std::move(Mod), &Error)); - if (!EE) { - llvm::errs() << "unable to make execution engine: " << Error << "\n"; - return 255; - } - - llvm::Function *EntryFn = M.getFunction("main"); - if (!EntryFn) { - llvm::errs() << "'main' function not found in module.\n"; - return 255; - } - - // FIXME: Support passing arguments. - std::vector Args; - Args.push_back(M.getModuleIdentifier()); - - EE->finalizeObject(); - return EE->runFunctionAsMain(EntryFn, Args, envp); -} +} // namespace interpreter int main(int argc, const char **argv, char * const *envp) { - void *MainAddr = (void*) (intptr_t) GetExecutablePath; - std::string Path = GetExecutablePath(argv[0]); + // This just needs to be some symbol in the binary; C++ doesn't + // allow taking the address of ::main however. + void *MainAddr = (void*) (intptr_t) interpreter::GetExecutablePath; + std::string Path = interpreter::GetExecutablePath(argv[0], MainAddr); IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); TextDiagnosticPrinter *DiagClient = new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); @@ -87,12 +75,15 @@ IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient); - // Use ELF on windows for now. - std::string TripleStr = llvm::sys::getProcessTriple(); + const std::string TripleStr = llvm::sys::getProcessTriple(); llvm::Triple T(TripleStr); + + // Use ELF on Windows-32 and MingW for now. +#ifndef CLANG_INTERPRETER_COFF_FORMAT if (T.isOSBinFormatCOFF()) T.setObjectFormat(llvm::Triple::ELF); - +#endif + Driver TheDriver(Path, T.str(), Diags); TheDriver.setTitle("clang interpreter"); TheDriver.setCheckInputsExist(false); @@ -163,12 +154,36 @@ if (!Clang.ExecuteAction(*Act)) return 1; + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + int Res = 255; - if (std::unique_ptr Module = Act->takeModule()) - Res = Execute(std::move(Module), envp); + if (std::unique_ptr Module = Act->takeModule()) { + llvm::Function *EntryFn = Module->getFunction("main"); + if (!EntryFn) { + llvm::errs() << "'main' function not found in module.\n"; + return Res; + } + + std::string Error; + std::unique_ptr EE( + interpreter::createExecutionEngine(std::move(Module), &Error)); + if (!EE) { + llvm::errs() << "unable to make execution engine: " << Error << "\n"; + return Res; + } + + interpreter::InvokeArgs Args; + for (int I = 1; I < argc; ++I) + Args.push_back(argv[I]); + + if (Clang.getLangOpts().CPlusPlus) + Res = interpreter::TryIt(EE.get(), EntryFn, Args, envp, interpreter::Invoke); + else + Res = interpreter::Invoke(EE.get(), EntryFn, Args, envp); + } // Shutdown. - llvm::llvm_shutdown(); return Res;