Files
FEX-Emu--FEX/Source/Interface/Core/LLVMJIT/LLVMCore.cpp
T
Ryan Houdek a8d2cb4a9f Fixes LLVM JIT
Doesn't yet work with multiblock
2020-03-06 07:55:44 +02:00

1896 lines
67 KiB
C++

#include "Interface/Context/Context.h"
#include "Interface/Core/DebugData.h"
#include "Interface/Core/LLVMJIT/LLVMMemoryManager.h"
#include "Interface/HLE/Syscalls.h"
#include <FEXCore/Core/CPUBackend.h>
#include <llvm-c/Core.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/InitializePasses.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/IRPrintingPasses.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/Verifier.h>
#include <llvm/Passes/PassBuilder.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Vectorize.h>
#include <vector>
#define DESTMAP_AS_MAP 1
#if DESTMAP_AS_MAP
using DestMapType = std::unordered_map<uint64_t, llvm::Value*>;
#else
using DestMapType = std::vector<llvm::Value*>;
#endif
namespace FEXCore::CPU {
static void CPUIDRun_Thunk(CPUIDEmu::FunctionResults *Results, FEXCore::CPUIDEmu *Class, uint32_t Function) {
*Results = Class->RunFunction(Function);
}
static void SetExitState_Thunk(FEXCore::Core::InternalThreadState *Thread) {
Thread->State.RunningEvents.ShouldStop = true;
}
class LLVMJITCore final : public CPUBackend {
public:
explicit LLVMJITCore(FEXCore::Core::InternalThreadState *Thread);
~LLVMJITCore() override;
std::string GetName() override { return "JIT"; }
void* CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData) override ;
void *MapRegion(void *HostPtr, uint64_t GuestPtr, uint64_t Size) override {
return HostPtr;
}
bool NeedsOpDispatch() override { return true; }
private:
void HandleIR(FEXCore::IR::IRListView<true> const *IR, IR::NodeWrapperIterator *Node);
llvm::Value *CreateContextGEP(uint64_t Offset, uint8_t Size);
llvm::Value *CreateContextPtr(uint64_t Offset, uint8_t Size);
llvm::Value *CreateMemoryLoad(llvm::Value *Ptr);
void CreateMemoryStore(llvm::Value *Ptr, llvm::Value *Val);
void ValidateMemoryInVM(uint64_t Ptr, uint8_t Size, bool Load);
template<typename Type>
Type MemoryLoad_Validate(uint64_t Ptr);
template<typename Type>
void MemoryStore_Validate(uint64_t Ptr, Type Val);
void DebugPrint(uint64_t Val);
void DebugPrint128(__uint128_t Val);
FEXCore::Core::InternalThreadState *ThreadState;
FEXCore::Context::Context *CTX;
struct LLVMState {
LLVMContextRef ContextRef;
llvm::Module *MainModule;
llvm::EngineBuilder *MainEngineBuilder;
llvm::IRBuilder<> *IRBuilder;
LLVMMemoryManager *MemManager;
std::vector<llvm::ExecutionEngine*> Functions;
};
struct LLVMCurrentState {
llvm::Function *SyscallFunction;
llvm::Function *CPUIDFunction;
llvm::Function *ExitVMFunction;
llvm::Function *ValuePrinter;
llvm::Function *ValidateLoad8;
llvm::Function *ValidateLoad16;
llvm::Function *ValidateLoad32;
llvm::Function *ValidateLoad64;
llvm::Function *ValidateLoad128;
llvm::Function *ValidateStore8;
llvm::Function *ValidateStore16;
llvm::Function *ValidateStore32;
llvm::Function *ValidateStore64;
llvm::Function *ValidateStore128;
llvm::Function *DebugPrint;
llvm::Function *DebugPrint128;
llvm::Type *CPUStateType;
llvm::GlobalVariable *CPUStateVar;
llvm::LoadInst *CPUState;
llvm::BasicBlock *CurrentBlock;
std::vector<llvm::BasicBlock*> Blocks;
llvm::BasicBlock *ExitBlock;
};
LLVMState JITState;
LLVMCurrentState JITCurrentState;
llvm::LLVMContext *Con;
llvm::Function *Func;
// Intrinsics
llvm::CallInst *Popcount(llvm::Value *Arg) { return JITState.IRBuilder->CreateUnaryIntrinsic(llvm::Intrinsic::ctpop, Arg); }
llvm::CallInst *BSwap(llvm::Value *Arg) { return JITState.IRBuilder->CreateUnaryIntrinsic(llvm::Intrinsic::bswap, Arg); }
llvm::CallInst *CTTZ(llvm::Value *Arg) {
std::vector<llvm::Type*> ArgTypes = {
Arg->getType(),
};
std::vector<llvm::Value*> Args = {
Arg,
JITState.IRBuilder->getInt1(true),
};
return JITState.IRBuilder->CreateIntrinsic(llvm::Intrinsic::cttz, ArgTypes, Args);
}
llvm::CallInst *CTLZ(llvm::Value *Arg) {
std::vector<llvm::Type*> ArgTypes = {
Arg->getType(),
};
std::vector<llvm::Value*> Args = {
Arg,
JITState.IRBuilder->getInt1(true),
};
return JITState.IRBuilder->CreateIntrinsic(llvm::Intrinsic::ctlz, ArgTypes, Args);
}
llvm::CallInst *FSHL(llvm::Value *Val, llvm::Value *Val2, llvm::Value *Amt) {
std::vector<llvm::Type*> ArgTypes = {
Val->getType(),
};
std::vector<llvm::Value*> Args = {
Val,
Val2,
Amt,
};
return JITState.IRBuilder->CreateIntrinsic(llvm::Intrinsic::fshl, ArgTypes, Args);
}
llvm::CallInst *FSHR(llvm::Value *Val, llvm::Value *Val2, llvm::Value *Amt) {
std::vector<llvm::Type*> ArgTypes = {
Val->getType(),
};
std::vector<llvm::Value*> Args = {
Val,
Val2,
Amt,
};
return JITState.IRBuilder->CreateIntrinsic(llvm::Intrinsic::fshr, ArgTypes, Args);
}
llvm::CallInst *CycleCounter() {
return JITState.IRBuilder->CreateIntrinsic(llvm::Intrinsic::readcyclecounter, {}, {});
}
void CreateDebugPrint(llvm::Value *Val) {
std::vector<llvm::Value*> Args;
Args.emplace_back(JITState.IRBuilder->getInt64(reinterpret_cast<uint64_t>(this)));
Args.emplace_back(Val);
if (Val->getType()->getIntegerBitWidth() > 64)
JITState.IRBuilder->CreateCall(JITCurrentState.DebugPrint128, Args);
else
JITState.IRBuilder->CreateCall(JITCurrentState.DebugPrint, Args);
}
void CreateGlobalVariables(llvm::ExecutionEngine *Engine, llvm::Module *FunctionModule);
llvm::Value *CastVectorToType(llvm::Value *Arg, bool Integer, uint8_t RegisterSize, uint8_t ElementSize);
llvm::Value *CastToOpaqueStructure(llvm::Value *Arg, llvm::Type *DstType);
void SetDest(IR::OrderedNodeWrapper Op, llvm::Value *Val);
llvm::Value *GetSrc(IR::OrderedNodeWrapper Src);
DestMapType DestMap;
FEXCore::IR::IRListView<true> const *CurrentIR;
std::unordered_map<IR::OrderedNodeWrapper::NodeOffsetType, llvm::BasicBlock*> JumpTargets;
// Target Machines
const std::string arch = "x86-64";
const std::string cpu = "skylake";
const llvm::Triple TargetTriple{"x86_64", "unknown", "linux", "gnu"};
const llvm::SmallVector<std::string, 0> Attrs;
llvm::TargetMachine *LLVMTarget;
};
LLVMJITCore::LLVMJITCore(FEXCore::Core::InternalThreadState *Thread)
: ThreadState {Thread}
, CTX {Thread->CTX} {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
JITState.ContextRef = LLVMContextCreate();
Con = *llvm::unwrap(&JITState.ContextRef);
JITState.MainModule = new llvm::Module("Main Module", *Con);
JITState.IRBuilder = new llvm::IRBuilder<>(*Con);
JITState.MainEngineBuilder = new llvm::EngineBuilder(std::unique_ptr<llvm::Module>(JITState.MainModule));
JITState.MainEngineBuilder->setEngineKind(llvm::EngineKind::JIT);
LLVMTarget = JITState.MainEngineBuilder->selectTarget(
TargetTriple,
arch, cpu, Attrs);
JITState.MemManager = new LLVMMemoryManager();
CTX->Config.LLVM_MemoryValidation = false;
#if !DESTMAP_AS_MAP
DestMap.resize(0x1000);
#endif
}
LLVMJITCore::~LLVMJITCore() {
// MainEngineBuilder takes overship of MainModule
delete JITState.MainEngineBuilder;
delete JITState.IRBuilder;
// Causes fault when destroying MCJIT
//for (auto Module : JITState.Functions) {
// delete Module;
//}
LLVMContextDispose(JITState.ContextRef);
}
void LLVMJITCore::ValidateMemoryInVM(uint64_t Ptr, uint8_t Size, bool Load) {
uint64_t VirtualBase = CTX->MemoryMapper.GetBaseOffset<uint64_t>(0);
uint64_t VirtualEnd = VirtualBase + (1ULL << 36ULL);
if (Ptr < VirtualBase || (Ptr + Size) >= VirtualEnd) {
LogMan::Msg::A("Invalid memory load at 0x%016lx. Wasn't within virtual range [0x%016lx, 0x%015lx)", Ptr, VirtualBase, VirtualEnd);
}
LogMan::Msg::D("%s guestmem: 0x%lx", Load ? "Loading from" : "Storing", Ptr - VirtualBase);
}
void LLVMJITCore::DebugPrint(uint64_t Val) {
LogMan::Msg::I(">>>> Value in Arg: 0x%lx, %ld", Val, Val);
}
void LLVMJITCore::DebugPrint128(__uint128_t Val) {
LogMan::Msg::I(">>>Val: %016lx, %016lx", static_cast<uint64_t>(Val >> 64), static_cast<uint64_t>(Val));
}
template<typename Type>
Type LLVMJITCore::MemoryLoad_Validate(uint64_t Ptr) {
ValidateMemoryInVM(Ptr, sizeof(Type), true);
Type *TypedAddr = reinterpret_cast<Type*>(Ptr);
Type Ret = TypedAddr[0];
uint64_t Data;
memcpy(&Data, &Ret, sizeof(Data));
LogMan::Msg::D("\tLoading: 0x%016lx", Data);
return Ret;
}
template<typename Type>
void LLVMJITCore::MemoryStore_Validate(uint64_t Ptr, Type Val) {
ValidateMemoryInVM(Ptr, sizeof(Type), false);
Type *TypedAddr = reinterpret_cast<Type*>(Ptr);
TypedAddr[0] = Val;
uint64_t Data;
memcpy(&Data, &Val, sizeof(Data));
LogMan::Msg::D("\tStoring: 0x%016lx", Data);
}
llvm::Value *LLVMJITCore::CreateMemoryLoad(llvm::Value *Ptr) {
if (CTX->Config.LLVM_MemoryValidation) {
std::vector<llvm::Value*> Args;
Args.emplace_back(JITState.IRBuilder->getInt64(reinterpret_cast<uint64_t>(this)));
Args.emplace_back(Ptr);
unsigned PtrSize = Ptr->getType()->getPointerElementType()->getIntegerBitWidth();
switch (PtrSize) {
case 8: return JITState.IRBuilder->CreateCall(JITCurrentState.ValidateLoad8, Args);
case 16: return JITState.IRBuilder->CreateCall(JITCurrentState.ValidateLoad16, Args);
case 32: return JITState.IRBuilder->CreateCall(JITCurrentState.ValidateLoad32, Args);
case 64: return JITState.IRBuilder->CreateCall(JITCurrentState.ValidateLoad64, Args);
case 128: return JITState.IRBuilder->CreateCall(JITCurrentState.ValidateLoad128, Args);
default: LogMan::Msg::A("Unknown Load Size: %d", PtrSize); break;
}
}
return JITState.IRBuilder->CreateLoad(Ptr);
}
void LLVMJITCore::CreateMemoryStore(llvm::Value *Ptr, llvm::Value *Val) {
if (CTX->Config.LLVM_MemoryValidation) {
std::vector<llvm::Value*> Args;
Args.emplace_back(JITState.IRBuilder->getInt64(reinterpret_cast<uint64_t>(this)));
Args.emplace_back(Ptr);
Args.emplace_back(Val);
unsigned PtrSize = Ptr->getType()->getPointerElementType()->getIntegerBitWidth();
switch (PtrSize) {
case 8: JITState.IRBuilder->CreateCall(JITCurrentState.ValidateStore8, Args); break;
case 16: JITState.IRBuilder->CreateCall(JITCurrentState.ValidateStore16, Args); break;
case 32: JITState.IRBuilder->CreateCall(JITCurrentState.ValidateStore32, Args); break;
case 64: JITState.IRBuilder->CreateCall(JITCurrentState.ValidateStore64, Args); break;
case 128: JITState.IRBuilder->CreateCall(JITCurrentState.ValidateStore128, Args); break;
default: LogMan::Msg::A("Unknown Store Size: %d", PtrSize); break;
}
return;
}
JITState.IRBuilder->CreateStore(Val, Ptr);
}
void LLVMJITCore::CreateGlobalVariables(llvm::ExecutionEngine *Engine, llvm::Module *FunctionModule) {
using namespace llvm;
Type *voidTy = Type::getVoidTy(*Con);
Type *i8 = Type::getInt8Ty(*Con);
Type *i16 = Type::getInt16Ty(*Con);
Type *i32 = Type::getInt32Ty(*Con);
Type *i64 = Type::getInt64Ty(*Con);
Type *i128 = Type::getInt128Ty(*Con);
// Syscall Function
{
auto FuncType = FunctionType::get(i64,
{
i64, // Technically a this pointer
i64,
ArrayType::get(i64, 7)->getPointerTo(),
},
false);
JITCurrentState.SyscallFunction = Function::Create(FuncType,
Function::ExternalLinkage,
"Syscall",
FunctionModule);
using ClassPtrType = uint64_t (FEXCore::SyscallHandler::*)(FEXCore::Core::InternalThreadState *, FEXCore::HLE::SyscallArguments *);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &FEXCore::SyscallHandler::HandleSyscall;
Engine->addGlobalMapping(JITCurrentState.SyscallFunction, Ptr.Data);
}
// CPUID Function
{
auto FuncType = FunctionType::get(voidTy,
{
ArrayType::get(i32, 4)->getPointerTo(),
i64, // Technically this is a pointer
i32, // CPUID Function
},
false);
JITCurrentState.CPUIDFunction = Function::Create(FuncType,
Function::ExternalLinkage,
"CPUID",
FunctionModule);
using ClassPtrType = void (*)(FEXCore::CPUIDEmu::FunctionResults*, FEXCore::CPUIDEmu*, uint32_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &CPUIDRun_Thunk;
Engine->addGlobalMapping(JITCurrentState.CPUIDFunction, Ptr.Data);
}
// Exit VM function
{
auto FuncType = FunctionType::get(voidTy,
{
i64, // Technically this is a pointer
},
false);
JITCurrentState.ExitVMFunction = Function::Create(FuncType,
Function::ExternalLinkage,
"ExitVM",
FunctionModule);
using ClassPtrType = void (*)(FEXCore::Core::InternalThreadState *Thread);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &SetExitState_Thunk;
Engine->addGlobalMapping(JITCurrentState.ExitVMFunction, Ptr.Data);
}
if (CTX->Config.LLVM_MemoryValidation) {
// Memory validate load 8
{
auto FuncType = FunctionType::get(i8,
{i64, // this pointer
i8->getPointerTo()}, false);
JITCurrentState.ValidateLoad8 = Function::Create(FuncType,
Function::ExternalLinkage,
"LoadValidate8",
FunctionModule);
using ClassPtrType = uint8_t (LLVMJITCore::*)(uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::MemoryLoad_Validate<uint8_t>;
Engine->addGlobalMapping(JITCurrentState.ValidateLoad8, Ptr.Data);
}
// Memory validate load 16
{
auto FuncType = FunctionType::get(i16,
{i64, // this pointer
i16->getPointerTo()}, false);
JITCurrentState.ValidateLoad16 = Function::Create(FuncType,
Function::ExternalLinkage,
"LoadValidate16",
FunctionModule);
using ClassPtrType = uint16_t (LLVMJITCore::*)(uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::MemoryLoad_Validate<uint16_t>;
Engine->addGlobalMapping(JITCurrentState.ValidateLoad16, Ptr.Data);
}
// Memory validate load 32
{
auto FuncType = FunctionType::get(i32,
{i64, // this pointer
i32->getPointerTo()}, false);
JITCurrentState.ValidateLoad32 = Function::Create(FuncType,
Function::ExternalLinkage,
"LoadValidate32",
FunctionModule);
using ClassPtrType = uint32_t (LLVMJITCore::*)(uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::MemoryLoad_Validate<uint32_t>;
Engine->addGlobalMapping(JITCurrentState.ValidateLoad32, Ptr.Data);
}
// Memory validate load 64
{
auto FuncType = FunctionType::get(i64,
{i64, // this pointer
i64->getPointerTo()}, false);
JITCurrentState.ValidateLoad64 = Function::Create(FuncType,
Function::ExternalLinkage,
"LoadValidate64",
FunctionModule);
using ClassPtrType = uint64_t (LLVMJITCore::*)(uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::MemoryLoad_Validate<uint64_t>;
Engine->addGlobalMapping(JITCurrentState.ValidateLoad64, Ptr.Data);
}
// Memory validate load 128
{
auto FuncType = FunctionType::get(i128,
{i64, // this pointer
i128->getPointerTo()}, false);
JITCurrentState.ValidateLoad128 = Function::Create(FuncType,
Function::ExternalLinkage,
"LoadValidate128",
FunctionModule);
using ClassPtrType = __uint128_t (LLVMJITCore::*)(uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::MemoryLoad_Validate<__uint128_t>;
Engine->addGlobalMapping(JITCurrentState.ValidateLoad128, Ptr.Data);
}
// Memory validate Store 8
{
auto FuncType = FunctionType::get(voidTy,
{i64, // this pointer
i8->getPointerTo(),
i8}, false);
JITCurrentState.ValidateStore8 = Function::Create(FuncType,
Function::ExternalLinkage,
"StoreValidate8",
FunctionModule);
using ClassPtrType = void (LLVMJITCore::*)(uint64_t, uint8_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::MemoryStore_Validate<uint8_t>;
Engine->addGlobalMapping(JITCurrentState.ValidateStore8, Ptr.Data);
}
// Memory validate Store 16
{
auto FuncType = FunctionType::get(voidTy,
{i64, // this pointer
i16->getPointerTo(),
i16}, false);
JITCurrentState.ValidateStore16 = Function::Create(FuncType,
Function::ExternalLinkage,
"StoreValidate16",
FunctionModule);
using ClassPtrType = void (LLVMJITCore::*)(uint64_t, uint16_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::MemoryStore_Validate<uint16_t>;
Engine->addGlobalMapping(JITCurrentState.ValidateStore16, Ptr.Data);
}
// Memory validate Store 32
{
auto FuncType = FunctionType::get(voidTy,
{i64, // this pointer
i32->getPointerTo(),
i32}, false);
JITCurrentState.ValidateStore32 = Function::Create(FuncType,
Function::ExternalLinkage,
"StoreValidate32",
FunctionModule);
using ClassPtrType = void (LLVMJITCore::*)(uint64_t, uint32_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::MemoryStore_Validate<uint32_t>;
Engine->addGlobalMapping(JITCurrentState.ValidateStore32, Ptr.Data);
}
// Memory validate Store 64
{
auto FuncType = FunctionType::get(voidTy,
{i64, // this pointer
i64->getPointerTo(),
i64}, false);
JITCurrentState.ValidateStore64 = Function::Create(FuncType,
Function::ExternalLinkage,
"StoreValidate64",
FunctionModule);
using ClassPtrType = void (LLVMJITCore::*)(uint64_t, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::MemoryStore_Validate<uint64_t>;
Engine->addGlobalMapping(JITCurrentState.ValidateStore64, Ptr.Data);
}
// Memory validate Store 128
{
auto FuncType = FunctionType::get(voidTy,
{i64, // this pointer
i128->getPointerTo(),
i128}, false);
JITCurrentState.ValidateStore128 = Function::Create(FuncType,
Function::ExternalLinkage,
"StoreValidate128",
FunctionModule);
using ClassPtrType = void (LLVMJITCore::*)(uint64_t, __uint128_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::MemoryStore_Validate<__uint128_t>;
Engine->addGlobalMapping(JITCurrentState.ValidateStore128, Ptr.Data);
}
}
// Value Print
{
auto FuncType = FunctionType::get(voidTy,
{i64, // this pointer
i64}, false);
JITCurrentState.DebugPrint = Function::Create(FuncType,
Function::ExternalLinkage,
"PrintVal",
FunctionModule);
using ClassPtrType = void (LLVMJITCore::*)(uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::DebugPrint;
Engine->addGlobalMapping(JITCurrentState.DebugPrint, Ptr.Data);
}
// Value Print 128
{
auto FuncType = FunctionType::get(voidTy,
{i64, // this pointer
i128}, false);
JITCurrentState.DebugPrint128 = Function::Create(FuncType,
Function::ExternalLinkage,
"PrintVal128",
FunctionModule);
using ClassPtrType = void (LLVMJITCore::*)(__uint128_t);
union PtrCast {
ClassPtrType ClassPtr;
void* Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &LLVMJITCore::DebugPrint128;
Engine->addGlobalMapping(JITCurrentState.DebugPrint128, Ptr.Data);
}
// JIT State
{
JITCurrentState.CPUStateType = StructType::create(*Con,
{
i64, // RIP
ArrayType::get(i64, 16), // Gregs
i64, // Pad to ensure alignment
ArrayType::get(i128, 16), // XMMs
i64, i64, // GS, FS
ArrayType::get(i8, 32), //rflags
},
"CPUStateType");
FunctionModule->getOrInsertGlobal("X86State::State", JITCurrentState.CPUStateType->getPointerTo());
JITCurrentState.CPUStateVar = FunctionModule->getNamedGlobal("X86State::State");
JITCurrentState.CPUStateVar->setConstant(true);
JITCurrentState.CPUStateVar->setInitializer(
ConstantInt::getIntegerValue(
JITCurrentState.CPUStateType->getPointerTo(),
APInt(64, reinterpret_cast<uint64_t>(&ThreadState->State))));
JITCurrentState.CPUState = JITState.IRBuilder->CreateLoad(JITCurrentState.CPUStateVar, false, "X86State::State::Local");
}
}
llvm::Value *LLVMJITCore::CreateContextGEP(uint64_t Offset, uint8_t Size) {
std::vector<llvm::Value*> GEPValues = {
JITState.IRBuilder->getInt32(0), // First value in the pointer to CPUState
};
if (Offset == 0) { // RIP
if (Size != 8) return nullptr;
GEPValues.emplace_back(JITState.IRBuilder->getInt32(0));
}
else if (Offset >= offsetof(FEXCore::Core::CPUState, gregs) && Offset < offsetof(FEXCore::Core::CPUState, xmm)) {
if (Size != 8 || Offset % 8 != 0) return nullptr;
GEPValues.emplace_back(JITState.IRBuilder->getInt32(1));
GEPValues.emplace_back(JITState.IRBuilder->getInt32((Offset - offsetof(FEXCore::Core::CPUState, gregs)) / 8));
}
else if (Offset >= offsetof(FEXCore::Core::CPUState, xmm) && Offset < offsetof(FEXCore::Core::CPUState, gs)) {
if (Size != 16 || Offset % 16 != 0) return nullptr;
GEPValues.emplace_back(JITState.IRBuilder->getInt32(3));
GEPValues.emplace_back(JITState.IRBuilder->getInt32((Offset - offsetof(FEXCore::Core::CPUState, xmm)) / 16));
}
else if (Offset == offsetof(FEXCore::Core::CPUState, gs)) {
if (Size != 8) return nullptr;
GEPValues.emplace_back(JITState.IRBuilder->getInt32(4));
}
else if (Offset == offsetof(FEXCore::Core::CPUState, fs)) {
if (Size != 8) return nullptr;
GEPValues.emplace_back(JITState.IRBuilder->getInt32(5));
}
else if (Offset >= offsetof(FEXCore::Core::CPUState, flags)) {
if (Size != 1) return nullptr;
GEPValues.emplace_back(JITState.IRBuilder->getInt32(6));
GEPValues.emplace_back(JITState.IRBuilder->getInt32(Offset - offsetof(FEXCore::Core::CPUState, flags[0])));
}
else
LogMan::Msg::A("Unknown X86State GEP: 0x%lx", Offset);
return JITState.IRBuilder->CreateGEP(JITCurrentState.CPUState, GEPValues, "Context::Value");
}
llvm::Value *LLVMJITCore::CreateContextPtr(uint64_t Offset, uint8_t Size) {
llvm::Type *i8 = llvm::Type::getInt8Ty(*Con);
llvm::Type *i16 = llvm::Type::getInt16Ty(*Con);
llvm::Type *i32 = llvm::Type::getInt32Ty(*Con);
llvm::Type *i64 = llvm::Type::getInt64Ty(*Con);
llvm::Type *i128 = llvm::Type::getInt128Ty(*Con);
// Let's try to create our pointer with GEP
// This can only happen if we are a full value from the context and is aligned correctly
llvm::Value *GEPResult = CreateContextGEP(Offset, Size);
if (GEPResult) return GEPResult;
llvm::Value *StateBasePtr = JITState.IRBuilder->CreatePtrToInt(JITCurrentState.CPUState, i64);
StateBasePtr = JITState.IRBuilder->CreateAdd(StateBasePtr, JITState.IRBuilder->getInt64(Offset));
// Convert back to pointer of correct size
switch (Size) {
case 1: return JITState.IRBuilder->CreateIntToPtr(StateBasePtr, i8->getPointerTo());
case 2: return JITState.IRBuilder->CreateIntToPtr(StateBasePtr, i16->getPointerTo());
case 4: return JITState.IRBuilder->CreateIntToPtr(StateBasePtr, i32->getPointerTo());
case 8: return JITState.IRBuilder->CreateIntToPtr(StateBasePtr, i64->getPointerTo());
case 16: return JITState.IRBuilder->CreateIntToPtr(StateBasePtr, i128->getPointerTo());
default: LogMan::Msg::A("Unknown context pointer size: %d", Size); break;
}
return nullptr;
}
llvm::Value *LLVMJITCore::CastVectorToType(llvm::Value *Arg, bool Integer, uint8_t RegisterSize, uint8_t ElementSize) {
uint8_t NumElements = RegisterSize / ElementSize;
llvm::Type *ElementType;
if (Integer) {
ElementType = llvm::Type::getIntNTy(*Con, ElementSize * 8);
}
else {
if (ElementSize == 4) {
ElementType = llvm::Type::getFloatTy(*Con);
}
else {
ElementType = llvm::Type::getDoubleTy(*Con);
}
}
llvm::Type *VectorType = llvm::VectorType::get(ElementType, NumElements);
// This happens frequently
// If the source argument isn't of vector type then BitCast fails moving from Scalar->Vector domains
// Need to create a vector and insert elements in to that vector from the scalar type instead
if (!Arg->getType()->isVectorTy()) {
return JITState.IRBuilder->CreateBitCast(Arg, VectorType);
}
return JITState.IRBuilder->CreateBitCast(Arg, VectorType);
}
llvm::Value *LLVMJITCore::CastToOpaqueStructure(llvm::Value *Arg, llvm::Type *DstType) {
if (Arg->getType()->isVectorTy()) {
// First do a bitcast from the vector type to the same size integer
unsigned ElementSize = Arg->getType()->getVectorElementType()->getIntegerBitWidth();
unsigned NumElements = Arg->getType()->getVectorNumElements();
auto NewIntegerType = llvm::Type::getIntNTy(*Con, ElementSize * NumElements);
Arg = JITState.IRBuilder->CreateBitCast(Arg, NewIntegerType);
}
return JITState.IRBuilder->CreateZExtOrTrunc(Arg, DstType->getPointerElementType());
}
void LLVMJITCore::SetDest(IR::OrderedNodeWrapper Op, llvm::Value *Val) {
DestMap[Op.ID()] = Val;
}
llvm::Value *LLVMJITCore::GetSrc(IR::OrderedNodeWrapper Src) {
#if DESTMAP_AS_MAP
LogMan::Throw::A(DestMap.find(Src.ID()) != DestMap.end(), "Op had Src but wasn't added to the dest map");
#endif
auto DstPtr = DestMap[Src.ID()];
LogMan::Throw::A(DstPtr != nullptr, "Destmap had slot but wasn't allocated memory");
return DstPtr;
}
void LLVMJITCore::HandleIR(FEXCore::IR::IRListView<true> const *IR, IR::NodeWrapperIterator *Node) {
using namespace llvm;
uintptr_t ListBegin = CurrentIR->GetListData();
uintptr_t DataBegin = CurrentIR->GetData();
IR::OrderedNodeWrapper *WrapperOp = (*Node)();
IR::OrderedNode *RealNode = WrapperOp->GetNode(ListBegin);
FEXCore::IR::IROp_Header *IROp = RealNode->Op(DataBegin);
uint8_t OpSize = IROp->Size;
switch (IROp->Op) {
case IR::OP_ENDBLOCK: {
auto Op = IROp->C<IR::IROp_EndBlock>();
if (Op->RIPIncrement) {
auto DownCountValue = JITState.IRBuilder->CreateGEP(JITCurrentState.CPUState,
{
JITState.IRBuilder->getInt32(0),
JITState.IRBuilder->getInt32(0),
},
"RIPIncrement");
auto LoadRIP = JITState.IRBuilder->CreateLoad(DownCountValue);
auto NewValue = JITState.IRBuilder->CreateAdd(LoadRIP, JITState.IRBuilder->getInt64(Op->RIPIncrement));
JITState.IRBuilder->CreateStore(NewValue, DownCountValue);
}
break;
}
case IR::OP_BREAK: {
std::vector<llvm::Value*> Args;
// We need to pull this argument from the ExecuteCodeFunction
Args.emplace_back(Func->args().begin());
JITState.IRBuilder->CreateCall(JITCurrentState.ExitVMFunction, Args);
JITState.IRBuilder->CreateBr(JITCurrentState.ExitBlock);
break;
}
case IR::OP_EXITFUNCTION:
case IR::OP_ENDFUNCTION: {
JITState.IRBuilder->CreateBr(JITCurrentState.ExitBlock);
break;
}
case IR::OP_JUMP: {
auto Op = IROp->C<IR::IROp_Jump>();
JITState.IRBuilder->CreateBr(JumpTargets[Op->Header.Args[0].ID()]);
break;
}
case IR::OP_CONDJUMP: {
auto Op = IROp->C<IR::IROp_CondJump>();
auto Cond = GetSrc(Op->Header.Args[0]);
auto Comp = JITState.IRBuilder->CreateICmpNE(Cond, JITState.IRBuilder->getInt64(0));
JITState.IRBuilder->CreateCondBr(Comp, JumpTargets[Op->Header.Args[1].ID()], JumpTargets[Op->Header.Args[2].ID()]);
break;
}
case IR::OP_MOV: {
auto Op = IROp->C<IR::IROp_Mov>();
auto Src = GetSrc(Op->Header.Args[0]);
SetDest(*WrapperOp, Src);
break;
}
case IR::OP_SELECT: {
auto Op = IROp->C<IR::IROp_Select>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
auto ArgTrue = GetSrc(Op->Header.Args[2]);
auto ArgFalse = GetSrc(Op->Header.Args[3]);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType());
ArgFalse = JITState.IRBuilder->CreateZExtOrTrunc(ArgFalse, ArgTrue->getType());
Value *Cmp{};
switch (Op->Cond.Val) {
case FEXCore::IR::COND_EQ:
Cmp = JITState.IRBuilder->CreateICmpEQ(Src1, Src2);
break;
case FEXCore::IR::COND_NEQ:
Cmp = JITState.IRBuilder->CreateICmpNE(Src1, Src2);
break;
case FEXCore::IR::COND_GE:
Cmp = JITState.IRBuilder->CreateICmpUGE(Src1, Src2);
break;
case FEXCore::IR::COND_LT:
Cmp = JITState.IRBuilder->CreateICmpULT(Src1, Src2);
break;
case FEXCore::IR::COND_GT:
Cmp = JITState.IRBuilder->CreateICmpUGT(Src1, Src2);
break;
case FEXCore::IR::COND_LE:
Cmp = JITState.IRBuilder->CreateICmpULE(Src1, Src2);
break;
default: LogMan::Msg::A("Unknown Select Op Type: %d", Op->Cond); break;
}
auto Result = JITState.IRBuilder->CreateSelect(Cmp, ArgTrue, ArgFalse);
SetDest(*WrapperOp, Result);
break;
}
case FEXCore::IR::IROps::OP_CONSTANT: {
auto Op = IROp->C<IR::IROp_Constant>();
auto Result = JITState.IRBuilder->getInt64(Op->Constant);
SetDest(*WrapperOp, Result);
break;
}
case FEXCore::IR::IROps::OP_SYSCALL: {
auto Op = IROp->C<IR::IROp_Syscall>();
std::vector<llvm::Value*> Args;
Args.emplace_back(JITState.IRBuilder->getInt64(reinterpret_cast<uint64_t>(&CTX->SyscallHandler)));
// We need to pull this argument from the ExecuteCodeFunction
Args.emplace_back(Func->args().begin());
auto LLVMArgs = JITState.IRBuilder->CreateAlloca(ArrayType::get(Type::getInt64Ty(*Con), 7));
for (unsigned i = 0; i < 7; ++i) {
auto Location = JITState.IRBuilder->CreateGEP(LLVMArgs,
{
JITState.IRBuilder->getInt32(0),
JITState.IRBuilder->getInt32(i),
},
"Arg");
auto Src = GetSrc(Op->Header.Args[i]);
JITState.IRBuilder->CreateStore(Src, Location);
}
Args.emplace_back(LLVMArgs);
auto Result = JITState.IRBuilder->CreateCall(JITCurrentState.SyscallFunction, Args);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_CPUID: {
auto Op = IROp->C<IR::IROp_CPUID>();
auto Src = GetSrc(Op->Header.Args[0]);
std::vector<llvm::Value*> Args{};
auto ReturnType = ArrayType::get(Type::getInt32Ty(*Con), 4);
auto LLVMArgs = JITState.IRBuilder->CreateAlloca(ReturnType);
Args.emplace_back(LLVMArgs);
Args.emplace_back(JITState.IRBuilder->getInt64(reinterpret_cast<uint64_t>(&CTX->CPUID)));
Args.emplace_back(Src);
JITState.IRBuilder->CreateCall(JITCurrentState.CPUIDFunction, Args);
auto Result = JITState.IRBuilder->CreateLoad(ReturnType, LLVMArgs);
SetDest(*WrapperOp, Result);
break;
}
// The IR's current representation of vectors is actually an array
case IR::OP_EXTRACTELEMENT: {
auto Op = IROp->C<IR::IROp_ExtractElement>();
auto Src = GetSrc(Op->Header.Args[0]);
std::vector<unsigned> Idxs = {Op->Idx};
auto Result = JITState.IRBuilder->CreateExtractValue(Src, Idxs);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_LOADCONTEXT: {
auto Op = IROp->C<IR::IROp_LoadContext>();
auto Value = CreateContextPtr(Op->Offset, Op->Size);
llvm::Value *Load;
if ((Op->Offset % Op->Size) == 0)
Load = JITState.IRBuilder->CreateAlignedLoad(Value, Op->Size);
else
Load = JITState.IRBuilder->CreateLoad(Value);
SetDest(*WrapperOp, Load);
break;
}
case IR::OP_STORECONTEXT: {
auto Op = IROp->C<IR::IROp_StoreContext>();
auto Src = GetSrc(Op->Header.Args[0]);
auto Value = CreateContextPtr(Op->Offset, Op->Size);
Src = CastToOpaqueStructure(Src, Value->getType());
if ((Op->Offset % Op->Size) == 0)
JITState.IRBuilder->CreateAlignedStore(Src, Value, Op->Size);
else
JITState.IRBuilder->CreateStore(Src, Value);
break;
}
case IR::OP_LOADFLAG: {
auto Op = IROp->C<IR::IROp_LoadFlag>();
auto Value = CreateContextPtr(offsetof(FEXCore::Core::CPUState, flags) + Op->Flag, 1);
auto Load = JITState.IRBuilder->CreateLoad(Value);
SetDest(*WrapperOp, Load);
break;
}
case IR::OP_STOREFLAG: {
auto Op = IROp->C<IR::IROp_StoreFlag>();
auto Src = GetSrc(Op->Header.Args[0]);
auto Value = CreateContextPtr(offsetof(FEXCore::Core::CPUState, flags) + Op->Flag, 1);
Src = JITState.IRBuilder->CreateZExtOrTrunc(Src, Type::getInt8Ty(*Con));
Src = JITState.IRBuilder->CreateAnd(Src, JITState.IRBuilder->getInt8(1));
JITState.IRBuilder->CreateStore(Src, Value);
break;
}
case IR::OP_ADD: {
auto Op = IROp->C<IR::IROp_Add>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType());
auto Result = JITState.IRBuilder->CreateAdd(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_SUB: {
auto Op = IROp->C<IR::IROp_Add>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType());
auto Result = JITState.IRBuilder->CreateSub(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_XOR: {
auto Op = IROp->C<IR::IROp_Xor>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType());
auto Result = JITState.IRBuilder->CreateXor(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_BFE: {
auto Op = IROp->C<IR::IROp_Bfe>();
auto Src = GetSrc(Op->Header.Args[0]);
LogMan::Throw::A(OpSize <= 16, "OpSize is too large for BFE: %d", OpSize);
auto BitWidth = Src->getType()->getIntegerBitWidth();
if (OpSize == 16) {
LogMan::Throw::A(Op->Width <= 64, "Can't extract width of %d", Op->Width);
// Generate our 128bit mask
auto SourceMask = JITState.IRBuilder->CreateShl(JITState.IRBuilder->getIntN(BitWidth, 1), JITState.IRBuilder->getIntN(BitWidth, Op->Width));
SourceMask = JITState.IRBuilder->CreateSub(SourceMask, JITState.IRBuilder->getIntN(BitWidth, 1));
// Shift the source in to the correct location
auto Result = JITState.IRBuilder->CreateLShr(Src, JITState.IRBuilder->getIntN(BitWidth, Op->lsb));
// Mask what we want
Result = JITState.IRBuilder->CreateAnd(Result, SourceMask);
SetDest(*WrapperOp, Result);
}
else {
uint64_t SourceMask = (1ULL << Op->Width) - 1;
if (Op->Width == 64)
SourceMask = ~0ULL;
auto Result = JITState.IRBuilder->CreateLShr(Src, JITState.IRBuilder->getIntN(BitWidth, Op->lsb));
Result = JITState.IRBuilder->CreateAnd(Result,
JITState.IRBuilder->getIntN(BitWidth, SourceMask));
SetDest(*WrapperOp, Result);
}
break;
}
case IR::OP_BFI: {
auto Op = IROp->C<IR::IROp_Bfi>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
uint64_t SourceMask = (1ULL << Op->Width) - 1;
if (Op->Width == 64)
SourceMask = ~0ULL;
uint64_t DestMask = ~(SourceMask << Op->lsb);
auto BitWidth = Src1->getType()->getIntegerBitWidth();
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType());
auto MaskedDest = JITState.IRBuilder->CreateAnd(Src1, JITState.IRBuilder->getIntN(BitWidth, DestMask));
auto MaskedSrc = JITState.IRBuilder->CreateAnd(Src2, JITState.IRBuilder->getIntN(BitWidth, SourceMask));
MaskedSrc = JITState.IRBuilder->CreateShl(MaskedSrc, JITState.IRBuilder->getIntN(BitWidth, Op->lsb));
auto Result = JITState.IRBuilder->CreateOr(MaskedDest, MaskedSrc);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_LSHR: {
auto Op = IROp->C<IR::IROp_Lshr>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Our IR assumes defined behaviour for shifting all the bits out of the value
// So we need to ZEXT to the next size up and then trunc
auto OriginalType = Src1->getType();
auto BiggerType = Type::getIntNTy(*Con, 128);
Src1 = JITState.IRBuilder->CreateZExt(Src1, BiggerType);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, BiggerType);
auto Result = JITState.IRBuilder->CreateLShr(Src1, Src2);
Result = JITState.IRBuilder->CreateTrunc(Result, OriginalType);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_ASHR: {
auto Op = IROp->C<IR::IROp_Ashr>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Our IR assumes defined behaviour for shifting all the bits out of the value
// So we need to ZEXT to the next size up and then trunc
auto OriginalType = Src1->getType();
auto BiggerType = Type::getIntNTy(*Con, 128);
Src1 = JITState.IRBuilder->CreateSExt(Src1, BiggerType);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, BiggerType);
auto Result = JITState.IRBuilder->CreateAShr(Src1, Src2);
Result = JITState.IRBuilder->CreateTrunc(Result, OriginalType);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_LSHL: {
auto Op = IROp->C<IR::IROp_Lshl>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Our IR assumes defined behaviour for shifting all the bits out of the value
// So we need to ZEXT to the next size up and then trunc
auto OriginalType = Src1->getType();
auto BiggerType = Type::getIntNTy(*Con, 128);
Src1 = JITState.IRBuilder->CreateZExt(Src1, BiggerType);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, BiggerType);
auto Result = JITState.IRBuilder->CreateShl(Src1, Src2);
Result = JITState.IRBuilder->CreateTrunc(Result, OriginalType);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_AND: {
auto Op = IROp->C<IR::IROp_And>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType());
auto Result = JITState.IRBuilder->CreateAnd(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_UMUL:
case IR::OP_MUL: {
auto Op = IROp->C<IR::IROp_Mul>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType());
auto Result = JITState.IRBuilder->CreateMul(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_ROL: {
auto Op = IROp->C<IR::IROp_Rol>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType());
auto Result = FSHL(Src1, Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_ROR: {
auto Op = IROp->C<IR::IROp_Ror>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType());
auto Result = FSHR(Src1, Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_PRINT: {
auto Op = IROp->C<IR::IROp_Print>();
auto Src = GetSrc(Op->Header.Args[0]);
if (Src->getType()->getIntegerBitWidth() < 64) {
Src = JITState.IRBuilder->CreateZExtOrTrunc(Src, Type::getInt64Ty(*Con));
}
CreateDebugPrint(Src);
break;
}
case IR::OP_CYCLECOUNTER: {
#ifdef DEBUG_CYCLES
SetDest(*WrapperOp, JITState.IRBuilder->getInt64(0));
#else
SetDest(*WrapperOp, CycleCounter());
#endif
break;
}
case IR::OP_POPCOUNT: {
auto Op = IROp->C<IR::IROp_Popcount>();
auto Src = GetSrc(Op->Header.Args[0]);
SetDest(*WrapperOp, Popcount(Src));
break;
}
case IR::OP_FINDLSB: {
auto Op = IROp->C<IR::IROp_FindLSB>();
auto Src = GetSrc(Op->Header.Args[0]);
unsigned SrcBitWidth = Src->getType()->getIntegerBitWidth();
llvm::Value *Result = CTTZ(Src);
// Need to compare source to zero, since we are expecting -1 on zero, llvm CTTZ returns undef on zero
auto Comp = JITState.IRBuilder->CreateICmpEQ(Src, JITState.IRBuilder->getIntN(SrcBitWidth, 0));
Result = JITState.IRBuilder->CreateSelect(Comp, JITState.IRBuilder->getIntN(SrcBitWidth, ~0ULL), Result);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_FINDMSB: {
auto Op = IROp->C<IR::IROp_FindMSB>();
auto Src = GetSrc(Op->Header.Args[0]);
unsigned SrcBitWidth = Src->getType()->getIntegerBitWidth();
llvm::Value *Result = CTLZ(Src);
Result = JITState.IRBuilder->CreateSub(JITState.IRBuilder->getIntN(SrcBitWidth, SrcBitWidth), Result);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_SEXT: {
auto Op = IROp->C<IR::IROp_Sext>();
auto Src = GetSrc(Op->Header.Args[0]);
llvm::Type *SourceType = Type::getIntNTy(*Con, Op->SrcSize);
llvm::Type *TargetType = Type::getIntNTy(*Con, OpSize * 8);
auto Result = JITState.IRBuilder->CreateSExtOrTrunc(Src, SourceType);
Result = JITState.IRBuilder->CreateSExt(Result, TargetType);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_ZEXT: {
auto Op = IROp->C<IR::IROp_Zext>();
auto Src = GetSrc(Op->Header.Args[0]);
llvm::Type *SourceType = Type::getIntNTy(*Con, Op->SrcSize);
llvm::Type *TargetType = Type::getIntNTy(*Con, OpSize * 8);
auto Result = JITState.IRBuilder->CreateZExtOrTrunc(Src, SourceType);
Result = JITState.IRBuilder->CreateZExt(Result, TargetType);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_OR: {
auto Op = IROp->C<IR::IROp_Or>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType());
auto Result = JITState.IRBuilder->CreateOr(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_UDIV: {
auto Op = IROp->C<IR::IROp_UDiv>();
auto Src = GetSrc(Op->Header.Args[0]);
auto Divisor = GetSrc(Op->Header.Args[1]);
Divisor = JITState.IRBuilder->CreateZExtOrTrunc(Divisor, Src->getType());
auto Result = JITState.IRBuilder->CreateUDiv(Src, Divisor);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_DIV: {
auto Op = IROp->C<IR::IROp_UDiv>();
auto Src = GetSrc(Op->Header.Args[0]);
auto Divisor = GetSrc(Op->Header.Args[1]);
Divisor = JITState.IRBuilder->CreateZExtOrTrunc(Divisor, Src->getType());
auto Result = JITState.IRBuilder->CreateSDiv(Src, Divisor);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_UREM: {
auto Op = IROp->C<IR::IROp_URem>();
auto Src = GetSrc(Op->Header.Args[0]);
auto Divisor = GetSrc(Op->Header.Args[1]);
Divisor = JITState.IRBuilder->CreateZExtOrTrunc(Divisor, Src->getType());
auto Result = JITState.IRBuilder->CreateURem(Src, Divisor);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_REM: {
auto Op = IROp->C<IR::IROp_Rem>();
auto Src = GetSrc(Op->Header.Args[0]);
auto Divisor = GetSrc(Op->Header.Args[1]);
Divisor = JITState.IRBuilder->CreateZExtOrTrunc(Divisor, Src->getType());
auto Result = JITState.IRBuilder->CreateSRem(Src, Divisor);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_LUDIV: {
auto Op = IROp->C<IR::IROp_LUDiv>();
// Each source is OpSize in size
// So you can have up to a 128bit divide from x86-64
auto SrcLow = GetSrc(Op->Header.Args[0]);
auto SrcHigh = GetSrc(Op->Header.Args[1]);
auto Divisor = GetSrc(Op->Header.Args[2]);
Type *iNormal = Type::getIntNTy(*Con, OpSize * 8);
Type *iLarge = Type::getIntNTy(*Con, OpSize * 8 * 2);
// Zero extend all values to large size
SrcLow = JITState.IRBuilder->CreateZExt(SrcLow, iLarge);
SrcHigh = JITState.IRBuilder->CreateZExt(SrcHigh, iLarge);
Divisor = JITState.IRBuilder->CreateZExt(Divisor, iLarge);
// Combine the split values
SrcHigh = JITState.IRBuilder->CreateShl(SrcHigh, JITState.IRBuilder->getIntN(OpSize * 8 * 2, OpSize * 8));
auto Dividend = JITState.IRBuilder->CreateOr(SrcHigh, SrcLow);
// Now do the divide
auto Result = JITState.IRBuilder->CreateUDiv(Dividend, Divisor);
// Now truncate back down origina size and store
Result = JITState.IRBuilder->CreateTrunc(Result, iNormal);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_LDIV: {
auto Op = IROp->C<IR::IROp_LDiv>();
// Each source is OpSize in size
// So you can have up to a 128bit divide from x86-64
auto SrcLow = GetSrc(Op->Header.Args[0]);
auto SrcHigh = GetSrc(Op->Header.Args[1]);
auto Divisor = GetSrc(Op->Header.Args[2]);
Type *iNormal = Type::getIntNTy(*Con, OpSize * 8);
Type *iLarge = Type::getIntNTy(*Con, OpSize * 8 * 2);
// Zero extend all values to large size
SrcLow = JITState.IRBuilder->CreateZExt(SrcLow, iLarge);
SrcHigh = JITState.IRBuilder->CreateZExt(SrcHigh, iLarge);
Divisor = JITState.IRBuilder->CreateSExt(Divisor, iLarge);
// Combine the split values
SrcHigh = JITState.IRBuilder->CreateShl(SrcHigh, JITState.IRBuilder->getIntN(OpSize * 8 * 2, OpSize * 8));
auto Dividend = JITState.IRBuilder->CreateOr(SrcHigh, SrcLow);
// Now do the divide
auto Result = JITState.IRBuilder->CreateSDiv(Dividend, Divisor);
// Now truncate back down origina size and store
Result = JITState.IRBuilder->CreateTrunc(Result, iNormal);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_LUREM: {
auto Op = IROp->C<IR::IROp_LURem>();
// Each source is OpOpSize in size
// So you can have up to a 128bit divide from x86-64
auto SrcLow = GetSrc(Op->Header.Args[0]);
auto SrcHigh = GetSrc(Op->Header.Args[1]);
auto Divisor = GetSrc(Op->Header.Args[2]);
Type *iNormal = Type::getIntNTy(*Con, OpSize * 8);
Type *iLarge = Type::getIntNTy(*Con, OpSize * 8 * 2);
// Zero extend all values to large size
SrcLow = JITState.IRBuilder->CreateZExt(SrcLow, iLarge);
SrcHigh = JITState.IRBuilder->CreateZExt(SrcHigh, iLarge);
Divisor = JITState.IRBuilder->CreateZExt(Divisor, iLarge);
// Combine the split values
SrcHigh = JITState.IRBuilder->CreateShl(SrcHigh, JITState.IRBuilder->getIntN(OpSize * 8 * 2, OpSize * 8));
auto Dividend = JITState.IRBuilder->CreateOr(SrcHigh, SrcLow);
// Now do the remainder
auto Result = JITState.IRBuilder->CreateURem(Dividend, Divisor);
// Now truncate back down origina size and store
Result = JITState.IRBuilder->CreateTrunc(Result, iNormal);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_LREM: {
auto Op = IROp->C<IR::IROp_LRem>();
// Each source is OpOpSize in size
// So you can have up to a 128bit divide from x86-64
auto SrcLow = GetSrc(Op->Header.Args[0]);
auto SrcHigh = GetSrc(Op->Header.Args[1]);
auto Divisor = GetSrc(Op->Header.Args[2]);
Type *iNormal = Type::getIntNTy(*Con, OpSize * 8);
Type *iLarge = Type::getIntNTy(*Con, OpSize * 8 * 2);
// Zero extend all values to large size
SrcLow = JITState.IRBuilder->CreateZExt(SrcLow, iLarge);
SrcHigh = JITState.IRBuilder->CreateZExt(SrcHigh, iLarge);
Divisor = JITState.IRBuilder->CreateSExt(Divisor, iLarge);
// Combine the split values
SrcHigh = JITState.IRBuilder->CreateShl(SrcHigh, JITState.IRBuilder->getIntN(OpSize * 8 * 2, OpSize * 8));
auto Dividend = JITState.IRBuilder->CreateOr(SrcHigh, SrcLow);
// Now do the remainder
auto Result = JITState.IRBuilder->CreateSRem(Dividend, Divisor);
// Now truncate back down origina size and store
Result = JITState.IRBuilder->CreateTrunc(Result, iNormal);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_UMULH: {
auto Op = IROp->C<IR::IROp_UMulH>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
Type *iNormal = Type::getIntNTy(*Con, OpSize * 8);
Type *iLarge = Type::getIntNTy(*Con, OpSize * 8 * 2);
// Zero extend all values to larger value
Src1 = JITState.IRBuilder->CreateZExt(Src1, iLarge);
Src2 = JITState.IRBuilder->CreateZExt(Src2, iLarge);
// Do the large multiply
auto Result = JITState.IRBuilder->CreateMul(Src1, Src2);
Result = JITState.IRBuilder->CreateLShr(Result, JITState.IRBuilder->getIntN(OpSize * 8 * 2, OpSize * 8));
// Now truncate back down to origianl size and store
Result = JITState.IRBuilder->CreateTrunc(Result, iNormal);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_MULH: {
auto Op = IROp->C<IR::IROp_MulH>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
Type *iNormal = Type::getIntNTy(*Con, OpSize * 8);
Type *iLarge = Type::getIntNTy(*Con, OpSize * 8 * 2);
// Sign extend all values to larger value
Src1 = JITState.IRBuilder->CreateSExt(Src1, iLarge);
Src2 = JITState.IRBuilder->CreateSExt(Src2, iLarge);
// Do the large multiply
auto Result = JITState.IRBuilder->CreateMul(Src1, Src2);
Result = JITState.IRBuilder->CreateLShr(Result, JITState.IRBuilder->getIntN(OpSize * 8 * 2, OpSize * 8));
// Now truncate back down to origianl size and store
Result = JITState.IRBuilder->CreateTrunc(Result, iNormal);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_REV: {
auto Op = IROp->C<IR::IROp_Rev>();
auto Src = GetSrc(Op->Header.Args[0]);
SetDest(*WrapperOp, BSwap(Src));
break;
}
case IR::OP_CREATEVECTOR2: {
auto Op = IROp->C<IR::IROp_CreateVector2>();
LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Value *Undef = UndefValue::get(VectorType::get(Src1->getType(), 2));
// Src1 = CastToOpaqueStructure(Src1, ElementType);
// Src2 = CastToOpaqueStructure(Src2, ElementType);
Undef = JITState.IRBuilder->CreateInsertElement(Undef, Src1, JITState.IRBuilder->getInt32(0));
Undef = JITState.IRBuilder->CreateInsertElement(Undef, Src2, JITState.IRBuilder->getInt32(1));
SetDest(*WrapperOp, Undef);
break;
}
case IR::OP_SPLATVECTOR2: {
auto Op = IROp->C<IR::IROp_SplatVector2>();
LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
auto Src = GetSrc(Op->Header.Args[0]);
auto Result = JITState.IRBuilder->CreateVectorSplat(2, Src);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_SPLATVECTOR3: {
auto Op = IROp->C<IR::IROp_SplatVector3>();
LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
auto Src = GetSrc(Op->Header.Args[0]);
auto Result = JITState.IRBuilder->CreateVectorSplat(3, Src);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_SPLATVECTOR4: {
auto Op = IROp->C<IR::IROp_SplatVector4>();
LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
auto Src = GetSrc(Op->Header.Args[0]);
auto Result = JITState.IRBuilder->CreateVectorSplat(4, Src);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VOR: {
auto Op = IROp->C<IR::IROp_Or>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
auto Result = JITState.IRBuilder->CreateOr(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VXOR: {
auto Op = IROp->C<IR::IROp_Or>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
auto Result = JITState.IRBuilder->CreateXor(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VADD: {
auto Op = IROp->C<IR::IROp_VAdd>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Src1 = CastVectorToType(Src1, true, Op->RegisterSize, Op->ElementSize);
Src2 = CastVectorToType(Src2, true, Op->RegisterSize, Op->ElementSize);
auto Result = JITState.IRBuilder->CreateAdd(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VSUB: {
auto Op = IROp->C<IR::IROp_VSub>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Src1 = CastVectorToType(Src1, true, Op->RegisterSize, Op->ElementSize);
Src2 = CastVectorToType(Src2, true, Op->RegisterSize, Op->ElementSize);
auto Result = JITState.IRBuilder->CreateSub(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VUSHL: {
auto Op = IROp->C<IR::IROp_VUShl>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Src1 = CastVectorToType(Src1, true, Op->RegisterSize, Op->ElementSize);
Src2 = CastVectorToType(Src2, true, Op->RegisterSize, Op->ElementSize);
// Now we will do a lshr <NumElements x i1> -> <NumElements x ElementSize>
auto Result = JITState.IRBuilder->CreateShl(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VUSHLS: {
auto Op = IROp->C<IR::IROp_VUShlS>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Src1 = CastVectorToType(Src1, true, Op->RegisterSize, Op->ElementSize);
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, Src1->getType()->getScalarType());
Src2 = JITState.IRBuilder->CreateVectorSplat(Op->RegisterSize / Op->ElementSize, Src2);
// Now we will do a lshr <NumElements x i1> -> <NumElements x ElementSize>
auto Result = JITState.IRBuilder->CreateShl(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VUSHR: {
auto Op = IROp->C<IR::IROp_VUShr>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Src1 = CastVectorToType(Src1, true, Op->RegisterSize, Op->ElementSize);
Src2 = JITState.IRBuilder->CreateVectorSplat(Op->RegisterSize / Op->ElementSize, Src2);
// Now we will do a lshr <NumElements x i1> -> <NumElements x ElementSize>
auto Result = JITState.IRBuilder->CreateLShr(Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VCMPEQ: {
auto Op = IROp->C<IR::IROp_VCMPEQ>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Src1 = CastVectorToType(Src1, true, Op->RegisterSize, Op->ElementSize);
Src2 = CastVectorToType(Src2, true, Op->RegisterSize, Op->ElementSize);
// Do an icmpeq, this will return a vector of <NumElements x i1>
auto Result = JITState.IRBuilder->CreateICmpEQ(Src1, Src2);
// Now we will do a sext <NumElements x i1> -> <NumElements x ElementSize>
Result = JITState.IRBuilder->CreateSExt(Result, Src1->getType());
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VCMPGT: {
auto Op = IROp->C<IR::IROp_VCMPGT>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Src1 = CastVectorToType(Src1, true, Op->RegisterSize, Op->ElementSize);
Src2 = CastVectorToType(Src2, true, Op->RegisterSize, Op->ElementSize);
// Do an icmpeq, this will return a vector of <NumElements x i1>
auto Result = JITState.IRBuilder->CreateICmpSGT(Src1, Src2);
// Now we will do a sext <NumElements x i1> -> <NumElements x ElementSize>
Result = JITState.IRBuilder->CreateSExt(Result, Src1->getType());
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VUMIN: {
auto Op = IROp->C<IR::IROp_VUMin>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Src1 = CastVectorToType(Src1, true, Op->RegisterSize, Op->ElementSize);
Src2 = CastVectorToType(Src2, true, Op->RegisterSize, Op->ElementSize);
auto Result = JITState.IRBuilder->CreateICmpULT(Src1, Src2);
Result = JITState.IRBuilder->CreateSelect(Result, Src1, Src2);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VZIP2:
case IR::OP_VZIP: {
auto Op = IROp->C<IR::IROp_VZip>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Src1 = CastVectorToType(Src1, true, Op->RegisterSize, Op->ElementSize);
Src2 = CastVectorToType(Src2, true, Op->RegisterSize, Op->ElementSize);
unsigned NumElements = Op->RegisterSize / Op->ElementSize;
unsigned BaseElement = IROp->Op == IR::OP_VZIP2 ? NumElements / 2 : 0;
std::vector<uint32_t> VectorMask;
for (unsigned i = 0; i < NumElements; ++i) {
unsigned shfl = i % 2 ? (NumElements + (i >> 1)) : (i >> 1);
shfl += BaseElement;
VectorMask.emplace_back(shfl);
}
auto VectorMaskConstant = ConstantDataVector::get(*Con, VectorMask);
auto Result = JITState.IRBuilder->CreateShuffleVector(Src1, Src2, VectorMaskConstant);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_VINSELEMENT: {
auto Op = IROp->C<IR::IROp_VInsElement>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
// Cast to the type we want
Src1 = CastVectorToType(Src1, true, Op->RegisterSize, Op->ElementSize);
Src2 = CastVectorToType(Src2, true, Op->RegisterSize, Op->ElementSize);
// Extract our source index
auto Source = JITState.IRBuilder->CreateExtractElement(Src2, JITState.IRBuilder->getInt32(Op->SrcIdx));
auto Result = JITState.IRBuilder->CreateInsertElement(Src1, Source, JITState.IRBuilder->getInt32(Op->DestIdx));
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_CAS: {
auto Op = IROp->C<IR::IROp_CAS>();
auto Src1 = GetSrc(Op->Header.Args[0]);
auto Src2 = GetSrc(Op->Header.Args[1]);
auto MemSrc = GetSrc(Op->Header.Args[2]);
MemSrc = JITState.IRBuilder->CreateAdd(MemSrc, JITState.IRBuilder->getInt64(CTX->MemoryMapper.GetBaseOffset<uint64_t>(0)));
// Cast the pointer type correctly
MemSrc = JITState.IRBuilder->CreateIntToPtr(MemSrc, Type::getIntNTy(*Con, OpSize * 8)->getPointerTo());
Src1 = JITState.IRBuilder->CreateZExtOrTrunc(Src1, MemSrc->getType()->getPointerElementType());
Src2 = JITState.IRBuilder->CreateZExtOrTrunc(Src2, MemSrc->getType()->getPointerElementType());
llvm::Value *Result = JITState.IRBuilder->CreateAtomicCmpXchg(MemSrc, Src1, Src2, llvm::AtomicOrdering::SequentiallyConsistent, llvm::AtomicOrdering::SequentiallyConsistent);
// Result is a { <Type>, i1 } So we need to extract it first
// Behaves exactly like std::atomic::compare_exchange_strong(Desired (Src1), Src2) ? Src1 : Desired
Result = JITState.IRBuilder->CreateExtractValue(Result, {0});
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_LOADMEM: {
auto Op = IROp->C<IR::IROp_LoadMem>();
auto Src = GetSrc(Op->Header.Args[0]);
Src = JITState.IRBuilder->CreateAdd(Src, JITState.IRBuilder->getInt64(CTX->MemoryMapper.GetBaseOffset<uint64_t>(0)));
// Cast the pointer type correctly
Src = JITState.IRBuilder->CreateIntToPtr(Src, Type::getIntNTy(*Con, Op->Size * 8)->getPointerTo());
auto Result = CreateMemoryLoad(Src);
SetDest(*WrapperOp, Result);
break;
}
case IR::OP_STOREMEM: {
auto Op = IROp->C<IR::IROp_StoreMem>();
auto Dst = GetSrc(Op->Header.Args[0]);
auto Src = GetSrc(Op->Header.Args[1]);
Dst = JITState.IRBuilder->CreateAdd(Dst, JITState.IRBuilder->getInt64(CTX->MemoryMapper.GetBaseOffset<uint64_t>(0)));
auto Type = Type::getIntNTy(*Con, Op->Size * 8);
Src = JITState.IRBuilder->CreateZExtOrTrunc(Src, Type);
Dst = JITState.IRBuilder->CreateIntToPtr(Dst, Type->getPointerTo());
CreateMemoryStore(Dst, Src);
break;
}
case IR::OP_DUMMY:
break;
default:
LogMan::Msg::A("Unknown IR Op: %d(%s)", IROp->Op, FEXCore::IR::GetName(IROp->Op).data());
break;
}
}
void* FEXCore::CPU::LLVMJITCore::CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData) {
using namespace llvm;
JumpTargets.clear();
JITCurrentState.Blocks.clear();
CurrentIR = IR;
#if DESTMAP_AS_MAP
DestMap.clear();
#else
uintptr_t ListSize = CurrentIR->GetListSize();
if (ListSize > DestMap.size()) {
DestMap.resize(std::max(DestMap.size() * 2, ListSize));
}
#endif
std::ostringstream FunctionName;
FunctionName << "Function_0x";
FunctionName << std::hex << ThreadState->State.State.rip;
auto FunctionModule = new llvm::Module("Module", *Con);
auto EngineBuilder = llvm::EngineBuilder(std::unique_ptr<llvm::Module>(FunctionModule));
EngineBuilder.setEngineKind(llvm::EngineKind::JIT);
EngineBuilder.setMCJITMemoryManager(std::unique_ptr<llvm::RTDyldMemoryManager>(JITState.MemManager));
auto Engine = EngineBuilder.create(LLVMTarget);
Type *i64 = Type::getInt64Ty(*Con);
auto FunctionType = FunctionType::get(Type::getVoidTy(*Con),
{
i64,
}, false);
Func = Function::Create(FunctionType,
Function::ExternalLinkage,
FunctionName.str(),
FunctionModule);
Func->setCallingConv(CallingConv::C);
auto Builder = JITState.IRBuilder;
auto Entry = BasicBlock::Create(*Con, "Entry", Func);
JITCurrentState.Blocks.emplace_back(Entry);
JITState.IRBuilder->SetInsertPoint(Entry);
JITCurrentState.CurrentBlock = Entry;
CreateGlobalVariables(Engine, FunctionModule);
uintptr_t ListBegin = CurrentIR->GetListData();
uintptr_t DataBegin = CurrentIR->GetData();
{
auto HeaderIterator = CurrentIR->begin();
IR::OrderedNodeWrapper *HeaderNodeWrapper = HeaderIterator();
IR::OrderedNode *HeaderNode = HeaderNodeWrapper->GetNode(ListBegin);
auto HeaderOp = HeaderNode->Op(DataBegin)->CW<FEXCore::IR::IROp_IRHeader>();
LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader");
IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin);
while (1) {
using namespace FEXCore::IR;
auto BlockIROp = BlockNode->Op(DataBegin)->CW<FEXCore::IR::IROp_CodeBlock>();
LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
auto Block = BasicBlock::Create(*Con, "Block", Func);
JITCurrentState.Blocks.emplace_back(Block);
JumpTargets[BlockNode->Wrapped(ListBegin).ID()] = Block;
if (BlockIROp->Next.ID() == 0) {
break;
} else {
BlockNode = BlockIROp->Next.GetNode(ListBegin);
}
}
}
// Let's create the exit block quick
JITCurrentState.ExitBlock = BasicBlock::Create(*Con, "ExitBlock", Func);
JITCurrentState.Blocks.emplace_back(JITCurrentState.ExitBlock);
JITState.IRBuilder->SetInsertPoint(JITCurrentState.ExitBlock);
Builder->CreateRetVoid();
auto HeaderIterator = CurrentIR->begin();
IR::OrderedNodeWrapper *HeaderNodeWrapper = HeaderIterator();
IR::OrderedNode *HeaderNode = HeaderNodeWrapper->GetNode(ListBegin);
auto HeaderOp = HeaderNode->Op(DataBegin)->CW<FEXCore::IR::IROp_IRHeader>();
LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader");
IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin);
bool First = true;
while (1) {
using namespace FEXCore::IR;
auto BlockIROp = BlockNode->Op(DataBegin)->CW<FEXCore::IR::IROp_CodeBlock>();
LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
// We grab these nodes this way so we can iterate easily
auto CodeBegin = CurrentIR->at(BlockIROp->Begin);
auto CodeLast = CurrentIR->at(BlockIROp->Last);
auto Block = JumpTargets[BlockNode->Wrapped(ListBegin).ID()];
if (First) {
JITState.IRBuilder->SetInsertPoint(Entry);
JITState.IRBuilder->CreateBr(Block);
First = false;
}
JITState.IRBuilder->SetInsertPoint(Block);
JITCurrentState.CurrentBlock = Block;
while (1) {
HandleIR(CurrentIR, &CodeBegin);
// CodeLast is inclusive. So we still need to dump the CodeLast op as well
if (CodeBegin == CodeLast) {
break;
}
++CodeBegin;
}
if (BlockIROp->Next.ID() == 0) {
break;
} else {
BlockNode = BlockIROp->Next.GetNode(ListBegin);
}
}
for (auto &Block : JITCurrentState.Blocks) {
// If the block is empty then let is just jump to the exit block
if (Block->empty()) {
JITState.IRBuilder->SetInsertPoint(Block);
JITState.IRBuilder->CreateBr(JITCurrentState.ExitBlock);
}
}
llvm::ModulePassManager FPM;
llvm::ModuleAnalysisManager FAM;
llvm::PassBuilder passBuilder(LLVMTarget);
passBuilder.registerModuleAnalyses(FAM);
passBuilder.buildModuleSimplificationPipeline(
llvm::PassBuilder::OptimizationLevel::O3,
llvm::PassBuilder::ThinLTOPhase::None);
raw_ostream &Out = outs();
if (CTX->Config.LLVM_PrinterPass)
{
FPM.addPass(PrintModulePass(Out));
}
if (CTX->Config.LLVM_IRValidation)
{
verifyModule(*FunctionModule, &Out);
}
FPM.run(*FunctionModule, FAM);
Engine->finalizeObject();
JITState.Functions.emplace_back(Engine);
DebugData->HostCodeSize = JITState.MemManager->GetLastCodeAllocation();
void *FunctionPtr = reinterpret_cast<void*>(Engine->getFunctionAddress(FunctionName.str()));
return FunctionPtr;
}
FEXCore::CPU::CPUBackend *CreateLLVMCore(FEXCore::Core::InternalThreadState *Thread) {
return new LLVMJITCore(Thread);
}
}