forked from OSchip/llvm-project
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 llvm-svn: 327528
This commit is contained in:
parent
3e3f5fb3a3
commit
529ce72efc
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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, char * const *envp) {
|
|||
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, char * const *envp) {
|
|||
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;
|
||||
|
|
Loading…
Reference in New Issue