r327528 - Expand clang-interpreter with example of throwing in and from the JIT for Windows64.

Frederich Munch via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 14 09:04:46 PDT 2018


Author: marsupial
Date: Wed Mar 14 09:04:45 2018
New Revision: 327528

URL: http://llvm.org/viewvc/llvm-project?rev=327528&view=rev
Log:
Expand clang-interpreter with example of throwing in and from the JIT for Windows64.

Summary:
Getting this to work is not particularly obvious, and having it as an example should be helpful.
Portions of this could be placed into LLVM, but as a whole it seems necessary to do this a higher level.

Reviewers: lhames, mehdi_amini

Reviewed By: lhames

Subscribers: mgrang, martell, cfe-commits, mgorny

Differential Revision: https://reviews.llvm.org/D35103

Added:
    cfe/trunk/examples/clang-interpreter/Invoke.cpp
    cfe/trunk/examples/clang-interpreter/Invoke.h
    cfe/trunk/examples/clang-interpreter/Manager.cpp
    cfe/trunk/examples/clang-interpreter/Manager.h
    cfe/trunk/examples/clang-interpreter/Test.cxx
Modified:
    cfe/trunk/examples/clang-interpreter/CMakeLists.txt
    cfe/trunk/examples/clang-interpreter/README.txt
    cfe/trunk/examples/clang-interpreter/main.cpp

Modified: cfe/trunk/examples/clang-interpreter/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/clang-interpreter/CMakeLists.txt?rev=327528&r1=327527&r2=327528&view=diff
==============================================================================
--- cfe/trunk/examples/clang-interpreter/CMakeLists.txt (original)
+++ cfe/trunk/examples/clang-interpreter/CMakeLists.txt Wed Mar 14 09:04:45 2018
@@ -3,13 +3,17 @@ set(LLVM_LINK_COMPONENTS
   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 @@ target_link_libraries(clang-interpreter
   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)

Added: cfe/trunk/examples/clang-interpreter/Invoke.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/clang-interpreter/Invoke.cpp?rev=327528&view=auto
==============================================================================
--- cfe/trunk/examples/clang-interpreter/Invoke.cpp (added)
+++ cfe/trunk/examples/clang-interpreter/Invoke.cpp Wed Mar 14 09:04:45 2018
@@ -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 <iostream>
+#include <stdexcept>
+
+namespace interpreter {
+
+int TryIt(llvm::ExecutionEngine *EE, llvm::Function *EntryFn,
+          const std::vector<std::string> &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;
+}
+
+}

Added: cfe/trunk/examples/clang-interpreter/Invoke.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/clang-interpreter/Invoke.h?rev=327528&view=auto
==============================================================================
--- cfe/trunk/examples/clang-interpreter/Invoke.h (added)
+++ cfe/trunk/examples/clang-interpreter/Invoke.h Wed Mar 14 09:04:45 2018
@@ -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 <string>
+#include <vector>
+
+namespace interpreter {
+
+typedef std::vector<std::string> 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

Added: cfe/trunk/examples/clang-interpreter/Manager.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/clang-interpreter/Manager.cpp?rev=327528&view=auto
==============================================================================
--- cfe/trunk/examples/clang-interpreter/Manager.cpp (added)
+++ cfe/trunk/examples/clang-interpreter/Manager.cpp Wed Mar 14 09:04:45 2018
@@ -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 <windows.h>
+#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<std::pair<DWORD, DWORD>> ImageRanges;
+  typedef std::map<uintptr_t, ImageRanges> 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<DWORD>::max(),
+                        std::numeric_limits<DWORD>::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<PRUNTIME_FUNCTION>(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<PRUNTIME_FUNCTION>(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<ULONG_PTR **>(CxxExcept);
+    EPtr--;
+
+    WINRTEXCEPTIONINFO **ppWei = reinterpret_cast<WINRTEXCEPTIONINFO **>(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

Added: cfe/trunk/examples/clang-interpreter/Manager.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/clang-interpreter/Manager.h?rev=327528&view=auto
==============================================================================
--- cfe/trunk/examples/clang-interpreter/Manager.h (added)
+++ cfe/trunk/examples/clang-interpreter/Manager.h Wed Mar 14 09:04:45 2018
@@ -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

Modified: cfe/trunk/examples/clang-interpreter/README.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/clang-interpreter/README.txt?rev=327528&r1=327527&r2=327528&view=diff
==============================================================================
--- cfe/trunk/examples/clang-interpreter/README.txt (original)
+++ cfe/trunk/examples/clang-interpreter/README.txt Wed Mar 14 09:04:45 2018
@@ -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 @@ It demonstrates the following features:
 
  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.

Added: cfe/trunk/examples/clang-interpreter/Test.cxx
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/clang-interpreter/Test.cxx?rev=327528&view=auto
==============================================================================
--- cfe/trunk/examples/clang-interpreter/Test.cxx (added)
+++ cfe/trunk/examples/clang-interpreter/Test.cxx Wed Mar 14 09:04:45 2018
@@ -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 <src>/tools/clang/examples/clang-interpreter/Test.cxx
+
+#include <stdexcept>
+#include <stdio.h>
+
+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;
+}

Modified: cfe/trunk/examples/clang-interpreter/main.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/clang-interpreter/main.cpp?rev=327528&r1=327527&r2=327528&view=diff
==============================================================================
--- cfe/trunk/examples/clang-interpreter/main.cpp (original)
+++ cfe/trunk/examples/clang-interpreter/main.cpp Wed Mar 14 09:04:45 2018
@@ -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 <memory>
+
 using namespace clang;
 using namespace clang::driver;
 
+namespace interpreter {
+
+static llvm::ExecutionEngine *
+createExecutionEngine(std::unique_ptr<llvm::Module> M, std::string *ErrorStr) {
+  llvm::EngineBuilder EB(std::move(M));
+  EB.setErrorStr(ErrorStr);
+  EB.setMemoryManager(llvm::make_unique<SingleSectionMemoryManager>());
+  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<std::string> &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<llvm::Module> M, std::string *ErrorStr) {
-  return llvm::EngineBuilder(std::move(M))
-      .setEngineKind(llvm::EngineKind::Either)
-      .setErrorStr(ErrorStr)
-      .create();
-}
-
-static int Execute(std::unique_ptr<llvm::Module> Mod, char *const *envp) {
-  llvm::InitializeNativeTarget();
-  llvm::InitializeNativeTargetAsmPrinter();
-
-  llvm::Module &M = *Mod;
-  std::string Error;
-  std::unique_ptr<llvm::ExecutionEngine> 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<std::string> 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<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
   TextDiagnosticPrinter *DiagClient =
     new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
@@ -87,12 +75,15 @@ int main(int argc, const char **argv, ch
   IntrusiveRefCntPtr<DiagnosticIDs> 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 @@ int main(int argc, const char **argv, ch
   if (!Clang.ExecuteAction(*Act))
     return 1;
 
+  llvm::InitializeNativeTarget();
+  llvm::InitializeNativeTargetAsmPrinter();
+
   int Res = 255;
-  if (std::unique_ptr<llvm::Module> Module = Act->takeModule())
-    Res = Execute(std::move(Module), envp);
+  if (std::unique_ptr<llvm::Module> 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<llvm::ExecutionEngine> 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;




More information about the cfe-commits mailing list