/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2013 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_CPU_BACKEND_X64_X64_EMITTER_H_ #define XENIA_CPU_BACKEND_X64_X64_EMITTER_H_ #include #include "xenia/base/arena.h" #include "xenia/cpu/function.h" #include "xenia/cpu/function_trace_data.h" #include "xenia/cpu/hir/hir_builder.h" #include "xenia/cpu/hir/instr.h" #include "xenia/cpu/hir/value.h" #include "xenia/memory.h" // NOTE: must be included last as it expects windows.h to already be included. #include "third_party/xbyak/xbyak/xbyak.h" #include "third_party/xbyak/xbyak/xbyak_util.h" namespace xe { namespace cpu { class Processor; } // namespace cpu } // namespace xe namespace xe { namespace cpu { namespace backend { namespace x64 { class X64Backend; class X64CodeCache; enum RegisterFlags { REG_DEST = (1 << 0), REG_ABCD = (1 << 1), }; enum XmmConst { XMMZero = 0, XMMOne, XMMNegativeOne, XMMFFFF, XMMMaskX16Y16, XMMFlipX16Y16, XMMFixX16Y16, XMMNormalizeX16Y16, XMM0001, XMM3301, XMM3333, XMMSignMaskPS, XMMSignMaskPD, XMMAbsMaskPS, XMMAbsMaskPD, XMMByteSwapMask, XMMByteOrderMask, XMMPermuteControl15, XMMPermuteByteMask, XMMPackD3DCOLORSat, XMMPackD3DCOLOR, XMMUnpackD3DCOLOR, XMMPackFLOAT16_2, XMMUnpackFLOAT16_2, XMMPackFLOAT16_4, XMMUnpackFLOAT16_4, XMMPackSHORT_Min, XMMPackSHORT_Max, XMMPackSHORT_2, XMMPackSHORT_4, XMMUnpackSHORT_2, XMMUnpackSHORT_4, XMMOneOver255, XMMMaskEvenPI16, XMMShiftMaskEvenPI16, XMMShiftMaskPS, XMMShiftByteMask, XMMSwapWordMask, XMMUnsignedDwordMax, XMM255, XMMPI32, XMMSignMaskI8, XMMSignMaskI16, XMMSignMaskI32, XMMSignMaskF32, XMMShortMinPS, XMMShortMaxPS, XMMIntMin, XMMIntMax, XMMIntMaxPD, }; // Unfortunately due to the design of xbyak we have to pass this to the ctor. class XbyakAllocator : public Xbyak::Allocator { public: virtual bool useProtect() const { return false; } }; enum X64EmitterFeatureFlags { kX64EmitAVX2 = 1 << 1, kX64EmitFMA = 1 << 2, kX64EmitLZCNT = 1 << 3, kX64EmitBMI2 = 1 << 4, kX64EmitF16C = 1 << 5, kX64EmitMovbe = 1 << 6, }; class X64Emitter : public Xbyak::CodeGenerator { public: X64Emitter(X64Backend* backend, XbyakAllocator* allocator); virtual ~X64Emitter(); Processor* processor() const { return processor_; } X64Backend* backend() const { return backend_; } static uintptr_t PlaceConstData(); static void FreeConstData(uintptr_t data); bool Emit(GuestFunction* function, hir::HIRBuilder* builder, uint32_t debug_info_flags, FunctionDebugInfo* debug_info, void** out_code_address, size_t* out_code_size, std::vector* out_source_map); public: // Reserved: rsp // Scratch: rax/rcx/rdx // xmm0-2 // Available: rbx, r12-r15 (save to get r8-r11, rbp, rsi, rdi?) // xmm6-xmm15 (save to get xmm3-xmm5) static const int GPR_COUNT = 5; static const int XMM_COUNT = 10; static void SetupReg(const hir::Value* v, Xbyak::Reg8& r) { auto idx = gpr_reg_map_[v->reg.index]; r = Xbyak::Reg8(idx); } static void SetupReg(const hir::Value* v, Xbyak::Reg16& r) { auto idx = gpr_reg_map_[v->reg.index]; r = Xbyak::Reg16(idx); } static void SetupReg(const hir::Value* v, Xbyak::Reg32& r) { auto idx = gpr_reg_map_[v->reg.index]; r = Xbyak::Reg32(idx); } static void SetupReg(const hir::Value* v, Xbyak::Reg64& r) { auto idx = gpr_reg_map_[v->reg.index]; r = Xbyak::Reg64(idx); } static void SetupReg(const hir::Value* v, Xbyak::Xmm& r) { auto idx = xmm_reg_map_[v->reg.index]; r = Xbyak::Xmm(idx); } Xbyak::Label& epilog_label() { return *epilog_label_; } void MarkSourceOffset(const hir::Instr* i); void DebugBreak(); void Trap(uint16_t trap_type = 0); void UnimplementedInstr(const hir::Instr* i); void Call(const hir::Instr* instr, GuestFunction* function); void CallIndirect(const hir::Instr* instr, const Xbyak::Reg64& reg); void CallExtern(const hir::Instr* instr, const Function* function); void CallNative(void* fn); void CallNative(uint64_t (*fn)(void* raw_context)); void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0)); void CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0), uint64_t arg0); void CallNativeSafe(void* fn); void SetReturnAddress(uint64_t value); Xbyak::Reg64 GetContextReg(); Xbyak::Reg64 GetMembaseReg(); void ReloadContext(); void ReloadMembase(); void nop(size_t length = 1); // TODO(benvanik): Label for epilog (don't use strings). // Moves a 64bit immediate into memory. bool ConstantFitsIn32Reg(uint64_t v); void MovMem64(const Xbyak::RegExp& addr, uint64_t v); Xbyak::Address GetXmmConstPtr(XmmConst id); void LoadConstantXmm(Xbyak::Xmm dest, float v); void LoadConstantXmm(Xbyak::Xmm dest, double v); void LoadConstantXmm(Xbyak::Xmm dest, const vec128_t& v); Xbyak::Address StashXmm(int index, const Xbyak::Xmm& r); bool IsFeatureEnabled(uint32_t feature_flag) const { return (feature_flags_ & feature_flag) != 0; } FunctionDebugInfo* debug_info() const { return debug_info_; } size_t stack_size() const { return stack_size_; } protected: void* Emplace(size_t stack_size, GuestFunction* function = nullptr); bool Emit(hir::HIRBuilder* builder, size_t* out_stack_size); void EmitGetCurrentThreadId(); void EmitTraceUserCallReturn(); protected: Processor* processor_ = nullptr; X64Backend* backend_ = nullptr; X64CodeCache* code_cache_ = nullptr; XbyakAllocator* allocator_ = nullptr; Xbyak::util::Cpu cpu_; uint32_t feature_flags_ = 0; Xbyak::Label* epilog_label_ = nullptr; hir::Instr* current_instr_ = nullptr; FunctionDebugInfo* debug_info_ = nullptr; uint32_t debug_info_flags_ = 0; FunctionTraceData* trace_data_ = nullptr; Arena source_map_arena_; size_t stack_size_ = 0; static const uint32_t gpr_reg_map_[GPR_COUNT]; static const uint32_t xmm_reg_map_[XMM_COUNT]; }; } // namespace x64 } // namespace backend } // namespace cpu } // namespace xe #endif // XENIA_CPU_BACKEND_X64_X64_EMITTER_H_