2016-06-22 15:37:51 +02:00
# ifdef LLVM_AVAILABLE
# include <unordered_map>
# include <map>
# include <unordered_set>
# include <set>
# include <array>
# include "types.h"
# include "StrFmt.h"
# include "File.h"
# include "Log.h"
2017-03-19 13:53:48 +01:00
# include "VirtualMemory.h"
2016-06-22 15:37:51 +02:00
# ifdef _MSC_VER
# pragma warning(push, 0)
# endif
# include "llvm/Support/TargetSelect.h"
# include "llvm/Support/FormattedStream.h"
# include "llvm/ExecutionEngine/ExecutionEngine.h"
# include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
# include "llvm/ExecutionEngine/JITEventListener.h"
# ifdef _MSC_VER
# pragma warning(pop)
# endif
# ifdef _WIN32
# include <Windows.h>
# endif
# include "JIT.h"
// Global LLVM context (thread-unsafe)
llvm : : LLVMContext g_llvm_ctx ;
// Size of virtual memory area reserved: 512 MB
static const u64 s_memory_size = 0x20000000 ;
// Try to reserve a portion of virtual memory in the first 2 GB address space beforehand, if possible.
static void * const s_memory = [ ] ( ) - > void *
{
2017-03-19 13:53:48 +01:00
for ( u64 addr = 0x10000000 ; addr < = 0x80000000 - s_memory_size ; addr + = 0x1000000 )
2016-06-22 15:37:51 +02:00
{
2017-03-19 13:53:48 +01:00
if ( auto ptr = utils : : memory_reserve ( s_memory_size , ( void * ) addr ) )
2016-06-22 15:37:51 +02:00
{
2017-03-19 13:53:48 +01:00
return ptr ;
2016-06-22 15:37:51 +02:00
}
}
2017-03-19 13:53:48 +01:00
return utils : : memory_reserve ( s_memory_size ) ;
2016-06-22 15:37:51 +02:00
} ( ) ;
2016-08-10 12:09:11 +02:00
// Code section
static u8 * s_code_addr ;
static u64 s_code_size ;
2016-06-22 15:37:51 +02:00
// EH frames
static u8 * s_unwind_info ;
static u64 s_unwind_size ;
# ifdef _WIN32
2017-02-26 16:56:31 +01:00
static std : : vector < std : : vector < RUNTIME_FUNCTION > > s_unwind ; // .pdata
2016-06-22 15:37:51 +02:00
# endif
// Helper class
struct MemoryManager final : llvm : : RTDyldMemoryManager
{
2017-02-26 16:56:31 +01:00
std : : unordered_map < std : : string , std : : uintptr_t > & m_link ;
2016-06-22 15:37:51 +02:00
2017-03-19 13:53:48 +01:00
std : : array < u8 , 16 > * m_tramps ;
2017-02-26 16:56:31 +01:00
MemoryManager ( std : : unordered_map < std : : string , std : : uintptr_t > & table )
: m_link ( table )
2017-03-19 13:53:48 +01:00
, m_next ( s_memory )
, m_tramps ( nullptr )
2016-06-22 15:37:51 +02:00
{
}
[[noreturn]] static void null ( )
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Null function " HERE ) ;
2016-06-22 15:37:51 +02:00
}
virtual u64 getSymbolAddress ( const std : : string & name ) override
{
2017-03-19 13:53:48 +01:00
auto & addr = m_link [ name ] ;
2016-06-22 15:37:51 +02:00
2017-03-19 13:53:48 +01:00
// Find function address
if ( ! addr )
2016-06-22 15:37:51 +02:00
{
2017-03-19 13:53:48 +01:00
addr = RTDyldMemoryManager : : getSymbolAddress ( name ) ;
if ( addr )
{
LOG_WARNING ( GENERAL , " LLVM: Symbol requested: %s -> 0x%016llx " , name , addr ) ;
}
else
{
// It's fine if some function is never called, for example.
LOG_ERROR ( GENERAL , " LLVM: Linkage failed: %s " , name ) ;
addr = ( u64 ) null ;
}
2016-06-22 15:37:51 +02:00
}
2017-03-19 13:53:48 +01:00
// Verify address for small code model
if ( ( u64 ) s_memory > 0x80000000 - s_memory_size ? ( u64 ) addr - ( u64 ) s_memory > = s_memory_size : addr > = 0x80000000 )
2017-03-11 17:49:32 +01:00
{
2017-03-19 13:53:48 +01:00
// Allocate memory for trampolines
if ( ! m_tramps )
{
m_tramps = reinterpret_cast < decltype ( m_tramps ) > ( m_next ) ;
utils : : memory_commit ( m_next , 4096 , utils : : protection : : wx ) ;
m_next = ( u8 * ) ( ( u64 ) m_next + 4096 ) ;
}
// Create a trampoline
auto & data = * m_tramps + + ;
data [ 0x0 ] = 0xff ; // JMP [rip+2]
data [ 0x1 ] = 0x25 ;
data [ 0x2 ] = 0x02 ;
data [ 0x3 ] = 0x00 ;
data [ 0x4 ] = 0x00 ;
data [ 0x5 ] = 0x00 ;
data [ 0x6 ] = 0x48 ; // MOV rax, imm64 (not executed)
data [ 0x7 ] = 0xb8 ;
std : : memcpy ( data . data ( ) + 8 , & addr , 8 ) ;
addr = ( u64 ) & data ;
// Reset pointer (memory page exhausted)
if ( ( ( u64 ) m_tramps % 4096 ) = = 0 )
{
m_tramps = nullptr ;
}
2017-03-11 17:49:32 +01:00
}
2017-03-19 13:53:48 +01:00
return addr ;
2016-06-22 15:37:51 +02:00
}
virtual u8 * allocateCodeSection ( std : : uintptr_t size , uint align , uint sec_id , llvm : : StringRef sec_name ) override
{
// Simple allocation
const u64 next = : : align ( ( u64 ) m_next + size , 4096 ) ;
if ( next > ( u64 ) s_memory + s_memory_size )
{
LOG_FATAL ( GENERAL , " LLVM: Out of memory (size=0x%llx, aligned 0x%x) " , size , align ) ;
return nullptr ;
}
2017-03-19 13:53:48 +01:00
utils : : memory_commit ( m_next , size , utils : : protection : : wx ) ;
2016-08-10 12:09:11 +02:00
s_code_addr = ( u8 * ) m_next ;
s_code_size = size ;
2017-02-26 16:56:31 +01:00
LOG_NOTICE ( GENERAL , " LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x) " , sec_id , sec_name . data ( ) , m_next , size , align ) ;
2016-06-22 15:37:51 +02:00
return ( u8 * ) std : : exchange ( m_next , ( void * ) next ) ;
}
virtual u8 * allocateDataSection ( std : : uintptr_t size , uint align , uint sec_id , llvm : : StringRef sec_name , bool is_ro ) override
{
// Simple allocation
const u64 next = : : align ( ( u64 ) m_next + size , 4096 ) ;
if ( next > ( u64 ) s_memory + s_memory_size )
{
LOG_FATAL ( GENERAL , " LLVM: Out of memory (size=0x%llx, aligned 0x%x) " , size , align ) ;
return nullptr ;
}
2016-08-10 12:09:11 +02:00
if ( ! is_ro )
{
LOG_ERROR ( GENERAL , " LLVM: Writeable data section not supported! " ) ;
}
2017-03-19 13:53:48 +01:00
utils : : memory_commit ( m_next , size ) ;
2016-06-22 15:37:51 +02:00
2017-02-26 16:56:31 +01:00
LOG_NOTICE ( GENERAL , " LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s) " , sec_id , sec_name . data ( ) , m_next , size , align , is_ro ? " ro " : " rw " ) ;
2016-06-22 15:37:51 +02:00
return ( u8 * ) std : : exchange ( m_next , ( void * ) next ) ;
}
virtual bool finalizeMemory ( std : : string * = nullptr ) override
{
2016-08-10 12:09:11 +02:00
// TODO: make only read-only sections read-only
2017-02-26 16:56:31 +01:00
//#ifdef _WIN32
// DWORD op;
// VirtualProtect(s_memory, (u64)m_next - (u64)s_memory, PAGE_READONLY, &op);
// VirtualProtect(s_code_addr, s_code_size, PAGE_EXECUTE_READ, &op);
//#else
// ::mprotect(s_memory, (u64)m_next - (u64)s_memory, PROT_READ);
// ::mprotect(s_code_addr, s_code_size, PROT_READ | PROT_EXEC);
//#endif
2016-06-22 15:37:51 +02:00
return false ;
}
virtual void registerEHFrames ( u8 * addr , u64 load_addr , std : : size_t size ) override
{
s_unwind_info = addr ;
s_unwind_size = size ;
return RTDyldMemoryManager : : registerEHFrames ( addr , load_addr , size ) ;
}
virtual void deregisterEHFrames ( u8 * addr , u64 load_addr , std : : size_t size ) override
{
LOG_ERROR ( GENERAL , " deregisterEHFrames() called " ) ; // Not expected
return RTDyldMemoryManager : : deregisterEHFrames ( addr , load_addr , size ) ;
}
~ MemoryManager ( )
{
# ifdef _WIN32
2017-02-26 16:56:31 +01:00
for ( auto & & unwind : s_unwind )
2016-06-22 15:37:51 +02:00
{
2017-02-26 16:56:31 +01:00
if ( ! RtlDeleteFunctionTable ( unwind . data ( ) ) )
{
LOG_FATAL ( GENERAL , " RtlDeleteFunctionTable() failed! Error %u " , GetLastError ( ) ) ;
}
2016-06-22 15:37:51 +02:00
}
2017-02-26 16:56:31 +01:00
s_unwind . clear ( ) ;
2016-06-22 15:37:51 +02:00
# else
// TODO: unregister EH frames if necessary
# endif
2017-03-19 13:53:48 +01:00
utils : : memory_decommit ( s_memory , s_memory_size ) ;
2016-06-22 15:37:51 +02:00
}
private :
2017-03-19 13:53:48 +01:00
void * m_next ;
2016-06-22 15:37:51 +02:00
} ;
// Helper class
struct EventListener final : llvm : : JITEventListener
{
2017-02-26 16:56:31 +01:00
std : : string path ;
2016-06-22 15:37:51 +02:00
virtual void NotifyObjectEmitted ( const llvm : : object : : ObjectFile & obj , const llvm : : RuntimeDyld : : LoadedObjectInfo & inf ) override
{
2017-02-26 16:56:31 +01:00
if ( ! path . empty ( ) )
{
const llvm : : StringRef elf = obj . getData ( ) ;
fs : : file ( path , fs : : rewrite ) . write ( elf . data ( ) , elf . size ( ) ) ;
}
2016-06-22 15:37:51 +02:00
}
} ;
static EventListener s_listener ;
2017-03-14 13:23:07 +01:00
jit_compiler : : jit_compiler ( std : : unordered_map < std : : string , std : : uintptr_t > init_linkage_info , std : : string _cpu )
2017-02-26 16:56:31 +01:00
: m_link ( std : : move ( init_linkage_info ) )
2017-03-14 13:23:07 +01:00
, m_cpu ( std : : move ( _cpu ) )
2016-06-22 15:37:51 +02:00
{
2016-08-15 15:29:38 +02:00
verify ( HERE ) , s_memory ;
2016-06-22 15:37:51 +02:00
// Initialization
llvm : : InitializeNativeTarget ( ) ;
llvm : : InitializeNativeTargetAsmPrinter ( ) ;
LLVMLinkInMCJIT ( ) ;
2017-03-14 13:23:07 +01:00
if ( m_cpu . empty ( ) )
{
m_cpu = llvm : : sys : : getHostCPUName ( ) ;
}
2016-06-22 15:37:51 +02:00
2017-02-26 16:56:31 +01:00
if ( m_cpu = = " skylake " )
{
m_cpu = " haswell " ;
}
std : : string result ;
m_engine . reset ( llvm : : EngineBuilder ( std : : make_unique < llvm : : Module > ( " " , g_llvm_ctx ) )
2016-06-22 15:37:51 +02:00
. setErrorStr ( & result )
2017-02-26 16:56:31 +01:00
. setMCJITMemoryManager ( std : : make_unique < MemoryManager > ( m_link ) )
2016-06-22 15:37:51 +02:00
. setOptLevel ( llvm : : CodeGenOpt : : Aggressive )
2017-03-19 13:53:48 +01:00
. setCodeModel ( llvm : : CodeModel : : Small )
2017-02-26 16:56:31 +01:00
. setMCPU ( m_cpu )
2016-06-22 15:37:51 +02:00
. create ( ) ) ;
if ( ! m_engine )
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " LLVM: Failed to create ExecutionEngine: %s " , result ) ;
2016-06-22 15:37:51 +02:00
}
m_engine - > setProcessAllSections ( true ) ; // ???
m_engine - > RegisterJITEventListener ( & s_listener ) ;
2017-02-26 16:56:31 +01:00
}
void jit_compiler : : load ( std : : unique_ptr < llvm : : Module > module , std : : unique_ptr < llvm : : object : : ObjectFile > object )
{
s_listener . path . clear ( ) ;
auto * module_ptr = module . get ( ) ;
m_engine - > addModule ( std : : move ( module ) ) ;
m_engine - > addObjectFile ( std : : move ( object ) ) ;
m_engine - > finalizeObject ( ) ;
m_map . clear ( ) ;
for ( auto & func : module_ptr - > functions ( ) )
{
const std : : string & name = func . getName ( ) ;
if ( ! m_link . count ( name ) )
{
// Register compiled function
m_map [ name ] = m_engine - > getFunctionAddress ( name ) ;
}
}
init ( ) ;
}
void jit_compiler : : make ( std : : unique_ptr < llvm : : Module > module , std : : string path )
{
s_listener . path = std : : move ( path ) ;
auto * module_ptr = module . get ( ) ;
m_engine - > addModule ( std : : move ( module ) ) ;
2016-06-22 15:37:51 +02:00
m_engine - > finalizeObject ( ) ;
2017-02-26 16:56:31 +01:00
m_map . clear ( ) ;
2016-06-22 15:37:51 +02:00
for ( auto & func : module_ptr - > functions ( ) )
{
if ( ! func . empty ( ) )
{
const std : : string & name = func . getName ( ) ;
// Register compiled function
m_map [ name ] = m_engine - > getFunctionAddress ( name ) ;
}
// Delete IR to lower memory consumption
func . deleteBody ( ) ;
}
2017-02-26 16:56:31 +01:00
init ( ) ;
}
void jit_compiler : : init ( )
{
2016-06-22 15:37:51 +02:00
# ifdef _WIN32
// Register .xdata UNWIND_INFO (.pdata section is empty for some reason)
std : : set < u64 > func_set ;
for ( const auto & pair : m_map )
{
func_set . emplace ( pair . second ) ;
}
const u64 base = ( u64 ) s_memory ;
const u8 * bits = s_unwind_info ;
2017-02-26 16:56:31 +01:00
std : : vector < RUNTIME_FUNCTION > unwind ;
unwind . reserve ( m_map . size ( ) ) ;
2016-06-22 15:37:51 +02:00
2016-08-10 12:09:11 +02:00
for ( const u64 addr : func_set )
2016-06-22 15:37:51 +02:00
{
2016-08-10 12:09:11 +02:00
// Find next function address
const auto _next = func_set . upper_bound ( addr ) ;
const u64 next = _next ! = func_set . end ( ) ? * _next : ( u64 ) s_code_addr + s_code_size ;
2016-06-22 15:37:51 +02:00
// Generate RUNTIME_FUNCTION record
RUNTIME_FUNCTION uw ;
uw . BeginAddress = static_cast < u32 > ( addr - base ) ;
uw . EndAddress = static_cast < u32 > ( next - base ) ;
uw . UnwindData = static_cast < u32 > ( ( u64 ) bits - base ) ;
2017-02-26 16:56:31 +01:00
unwind . emplace_back ( uw ) ;
2016-06-22 15:37:51 +02:00
// Parse .xdata UNWIND_INFO record
const u8 flags = * bits + + ; // Version and flags
const u8 prolog = * bits + + ; // Size of prolog
const u8 count = * bits + + ; // Count of unwind codes
const u8 frame = * bits + + ; // Frame Reg + Off
bits + = : : align ( std : : max < u8 > ( 1 , count ) , 2 ) * sizeof ( u16 ) ; // UNWIND_CODE array
if ( flags ! = 1 )
{
2016-08-10 12:09:11 +02:00
// Can't happen for trivial code
2016-06-22 15:37:51 +02:00
LOG_ERROR ( GENERAL , " LLVM: unsupported UNWIND_INFO version/flags (0x%02x) " , flags ) ;
break ;
}
LOG_TRACE ( GENERAL , " LLVM: .xdata at 0x%llx: function 0x%x..0x%x: p0x%02x, c0x%02x, f0x%02x " , uw . UnwindData + base , uw . BeginAddress + base , uw . EndAddress + base , prolog , count , frame ) ;
}
if ( s_unwind_info + s_unwind_size ! = bits )
{
2016-08-15 15:57:51 +02:00
LOG_ERROR ( GENERAL , " LLVM: .xdata analysis failed! (%p != %p) " , s_unwind_info + s_unwind_size , bits ) ;
2016-06-22 15:37:51 +02:00
}
2017-02-26 16:56:31 +01:00
else if ( ! RtlAddFunctionTable ( unwind . data ( ) , ( DWORD ) unwind . size ( ) , base ) )
2016-06-22 15:37:51 +02:00
{
2016-08-15 15:57:51 +02:00
LOG_ERROR ( GENERAL , " RtlAddFunctionTable(%p) failed! Error %u " , s_unwind_info , GetLastError ( ) ) ;
2016-06-22 15:37:51 +02:00
}
else
{
2017-02-26 16:56:31 +01:00
LOG_NOTICE ( GENERAL , " LLVM: UNWIND_INFO registered (%p, size=0x%llx) " , s_unwind_info , s_unwind_size ) ;
2016-06-22 15:37:51 +02:00
}
2017-02-26 16:56:31 +01:00
s_unwind . emplace_back ( std : : move ( unwind ) ) ;
2017-03-19 13:53:48 +01:00
# else
// TODO: register EH frames if necessary
2016-06-22 15:37:51 +02:00
# endif
}
jit_compiler : : ~ jit_compiler ( )
{
}
# endif