Refactoring SymbolInfo/FunctionInfo/Function into Symbol/Function.

This commit is contained in:
Ben Vanik
2015-08-05 21:50:02 -07:00
parent 48d6e6becf
commit eaa1a8ee3a
54 changed files with 715 additions and 1027 deletions

View File

@@ -15,8 +15,7 @@
namespace xe {
namespace cpu {
class DebugInfo;
class Function;
class FunctionInfo;
class GuestFunction;
namespace hir {
class HIRBuilder;
} // namespace hir
@@ -38,10 +37,9 @@ class Assembler {
virtual void Reset();
virtual bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder,
virtual bool Assemble(GuestFunction* function, hir::HIRBuilder* builder,
uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info,
Function** out_function) = 0;
std::unique_ptr<DebugInfo> debug_info) = 0;
protected:
Backend* backend_;

View File

@@ -16,6 +16,8 @@
namespace xe {
namespace cpu {
class GuestFunction;
class Module;
class Processor;
} // namespace cpu
} // namespace xe
@@ -46,6 +48,9 @@ class Backend {
virtual std::unique_ptr<Assembler> CreateAssembler() = 0;
virtual std::unique_ptr<GuestFunction> CreateGuestFunction(
Module* module, uint32_t address) = 0;
protected:
Processor* processor_;
MachineInfo machine_info_;

View File

@@ -12,7 +12,7 @@
#include <string>
#include "xenia/cpu/symbol_info.h"
#include "xenia/cpu/function.h"
namespace xe {
namespace cpu {
@@ -29,7 +29,7 @@ class CodeCache {
// Finds a function based on the given host PC (that may be within a
// function).
virtual FunctionInfo* LookupFunction(uint64_t host_pc) = 0;
virtual GuestFunction* LookupFunction(uint64_t host_pc) = 0;
// Finds platform-specific function unwind info for the given host PC.
virtual void* LookupUnwindInfo(uint64_t host_pc) = 0;

View File

@@ -66,30 +66,26 @@ void X64Assembler::Reset() {
Assembler::Reset();
}
bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
bool X64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder,
uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info,
Function** out_function) {
std::unique_ptr<DebugInfo> debug_info) {
SCOPE_profile_cpu_f("cpu");
// Reset when we leave.
xe::make_reset_scope(this);
// Create now, and populate as we go.
// We may throw it away if we fail.
auto fn = std::make_unique<X64Function>(symbol_info);
// Lower HIR -> x64.
void* machine_code = nullptr;
size_t code_size = 0;
if (!emitter_->Emit(symbol_info, builder, debug_info_flags, debug_info.get(),
machine_code, code_size, fn->source_map())) {
if (!emitter_->Emit(function, builder, debug_info_flags, debug_info.get(),
machine_code, code_size, function->source_map())) {
return false;
}
// Stash generated machine code.
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) {
DumpMachineCode(machine_code, code_size, fn->source_map(), &string_buffer_);
DumpMachineCode(machine_code, code_size, function->source_map(),
&string_buffer_);
debug_info->set_machine_code_disasm(string_buffer_.ToString());
string_buffer_.Reset();
}
@@ -102,11 +98,10 @@ bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
}
}
fn->set_debug_info(std::move(debug_info));
fn->Setup(reinterpret_cast<uint8_t*>(machine_code), code_size);
function->set_debug_info(std::move(debug_info));
static_cast<X64Function*>(function)
->Setup(reinterpret_cast<uint8_t*>(machine_code), code_size);
// Pass back ownership.
*out_function = fn.release();
return true;
}

View File

@@ -35,10 +35,9 @@ class X64Assembler : public Assembler {
void Reset() override;
bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder,
bool Assemble(GuestFunction* function, hir::HIRBuilder* builder,
uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info,
Function** out_function) override;
std::unique_ptr<DebugInfo> debug_info) override;
private:
void DumpMachineCode(void* machine_code, size_t code_size,

View File

@@ -10,8 +10,9 @@
#include "xenia/cpu/backend/x64/x64_backend.h"
#include "xenia/cpu/backend/x64/x64_assembler.h"
#include "xenia/cpu/backend/x64/x64_emitter.h"
#include "xenia/cpu/backend/x64/x64_code_cache.h"
#include "xenia/cpu/backend/x64/x64_emitter.h"
#include "xenia/cpu/backend/x64/x64_function.h"
#include "xenia/cpu/backend/x64/x64_sequences.h"
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
#include "xenia/cpu/processor.h"
@@ -106,6 +107,11 @@ std::unique_ptr<Assembler> X64Backend::CreateAssembler() {
return std::make_unique<X64Assembler>(this);
}
std::unique_ptr<GuestFunction> X64Backend::CreateGuestFunction(
Module* module, uint32_t address) {
return std::make_unique<X64Function>(module, address);
}
using namespace Xbyak;
X64ThunkEmitter::X64ThunkEmitter(X64Backend* backend, XbyakAllocator* allocator)

View File

@@ -56,6 +56,9 @@ class X64Backend : public Backend {
std::unique_ptr<Assembler> CreateAssembler() override;
std::unique_ptr<GuestFunction> CreateGuestFunction(Module* module,
uint32_t address) override;
private:
std::unique_ptr<X64CodeCache> code_cache_;

View File

@@ -17,6 +17,7 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/cpu/function.h"
namespace xe {
namespace cpu {
@@ -120,7 +121,7 @@ void* X64CodeCache::PlaceHostCode(uint32_t guest_address, void* machine_code,
void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
size_t code_size, size_t stack_size,
FunctionInfo* function_info) {
GuestFunction* function_info) {
// Hold a lock while we bump the pointers up. This is important as the
// unwind table requires entries AND code to be sorted in order.
size_t low_mark;
@@ -220,15 +221,15 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
return uint32_t(uintptr_t(data_address));
}
FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) {
GuestFunction* X64CodeCache::LookupFunction(uint64_t host_pc) {
uint32_t key = uint32_t(host_pc - kGeneratedCodeBase);
void* fn_entry = std::bsearch(
&key, generated_code_map_.data(), generated_code_map_.size() + 1,
sizeof(std::pair<uint32_t, FunctionInfo*>),
sizeof(std::pair<uint32_t, Function*>),
[](const void* key_ptr, const void* element_ptr) {
auto key = *reinterpret_cast<const uint32_t*>(key_ptr);
auto element =
reinterpret_cast<const std::pair<uint64_t, FunctionInfo*>*>(
reinterpret_cast<const std::pair<uint64_t, GuestFunction*>*>(
element_ptr);
if (key < (element->first >> 32)) {
return -1;
@@ -239,7 +240,8 @@ FunctionInfo* X64CodeCache::LookupFunction(uint64_t host_pc) {
}
});
if (fn_entry) {
return reinterpret_cast<const std::pair<uint64_t, FunctionInfo*>*>(fn_entry)
return reinterpret_cast<const std::pair<uint64_t, GuestFunction*>*>(
fn_entry)
->second;
} else {
return nullptr;

View File

@@ -50,10 +50,10 @@ class X64CodeCache : public CodeCache {
size_t code_size, size_t stack_size);
void* PlaceGuestCode(uint32_t guest_address, void* machine_code,
size_t code_size, size_t stack_size,
FunctionInfo* function_info);
GuestFunction* function_info);
uint32_t PlaceData(const void* data, size_t length);
FunctionInfo* LookupFunction(uint64_t host_pc) override;
GuestFunction* LookupFunction(uint64_t host_pc) override;
protected:
// All executable code falls within 0x80000000 to 0x9FFFFFFF, so we can
@@ -111,7 +111,7 @@ class X64CodeCache : public CodeCache {
// Sorted map by host PC base offsets to source function info.
// This can be used to bsearch on host PC to find the guest function.
// The key is [start address | end address].
std::vector<std::pair<uint64_t, FunctionInfo*>> generated_code_map_;
std::vector<std::pair<uint64_t, GuestFunction*>> generated_code_map_;
};
} // namespace x64

View File

@@ -18,6 +18,7 @@
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/base/platform_win.h"
#include "xenia/cpu/function.h"
// When enabled, this will use Windows 8 APIs to get unwind info.
// TODO(benvanik): figure out why the callback variant doesn't work.

View File

@@ -27,8 +27,9 @@
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
#include "xenia/cpu/cpu_flags.h"
#include "xenia/cpu/debug_info.h"
#include "xenia/cpu/function.h"
#include "xenia/cpu/processor.h"
#include "xenia/cpu/symbol_info.h"
#include "xenia/cpu/symbol.h"
#include "xenia/cpu/thread_state.h"
#include "xenia/debug/debugger.h"
#include "xenia/profiling.h"
@@ -87,7 +88,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
X64Emitter::~X64Emitter() = default;
bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
bool X64Emitter::Emit(GuestFunction* function, HIRBuilder* builder,
uint32_t debug_info_flags, DebugInfo* debug_info,
void*& out_code_address, size_t& out_code_size,
std::vector<SourceMapEntry>& out_source_map) {
@@ -96,6 +97,7 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
// Reset.
debug_info_ = debug_info;
debug_info_flags_ = debug_info_flags;
trace_data_ = &function->trace_data();
source_map_arena_.Reset();
// Fill the generator with code.
@@ -106,7 +108,7 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
// Copy the final code to the cache and relocate it.
out_code_size = getSize();
out_code_address = Emplace(stack_size, function_info);
out_code_address = Emplace(stack_size, function);
// Stash source map.
source_map_arena_.CloneContents(out_source_map);
@@ -114,16 +116,16 @@ bool X64Emitter::Emit(FunctionInfo* function_info, HIRBuilder* builder,
return true;
}
void* X64Emitter::Emplace(size_t stack_size, FunctionInfo* function_info) {
void* X64Emitter::Emplace(size_t stack_size, GuestFunction* function) {
// To avoid changing xbyak, we do a switcharoo here.
// top_ points to the Xbyak buffer, and since we are in AutoGrow mode
// it has pending relocations. We copy the top_ to our buffer, swap the
// pointer, relocate, then return the original scratch pointer for use.
uint8_t* old_address = top_;
void* new_address;
if (function_info) {
new_address = code_cache_->PlaceGuestCode(function_info->address(), top_,
size_, stack_size, function_info);
if (function) {
new_address = code_cache_->PlaceGuestCode(function->address(), top_, size_,
stack_size, function);
} else {
new_address = code_cache_->PlaceHostCode(0, top_, size_, stack_size);
}
@@ -177,8 +179,8 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
// Safe now to do some tracing.
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctions) {
// We require 32-bit addresses.
assert_true(uint64_t(debug_info_->trace_data().header()) < UINT_MAX);
auto trace_header = debug_info_->trace_data().header();
assert_true(uint64_t(trace_data_->header()) < UINT_MAX);
auto trace_header = trace_data_->header();
// Call count.
lock();
@@ -265,11 +267,10 @@ void X64Emitter::MarkSourceOffset(const Instr* i) {
}
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) {
auto trace_data = debug_info_->trace_data();
uint32_t instruction_index =
(entry->source_offset - trace_data.start_address()) / 4;
(entry->source_offset - trace_data_->start_address()) / 4;
lock();
inc(qword[low_address(trace_data.instruction_execute_counts() +
inc(qword[low_address(trace_data_->instruction_execute_counts() +
instruction_index * 8)]);
}
}
@@ -341,8 +342,7 @@ extern "C" uint64_t ResolveFunction(void* raw_context,
// TODO(benvanik): required?
assert_not_zero(target_address);
Function* fn = NULL;
thread_state->processor()->ResolveFunction(target_address, &fn);
auto fn = thread_state->processor()->ResolveFunction(target_address);
assert_not_null(fn);
auto x64_fn = static_cast<X64Function*>(fn);
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
@@ -350,11 +350,11 @@ extern "C" uint64_t ResolveFunction(void* raw_context,
return addr;
}
void X64Emitter::Call(const hir::Instr* instr, FunctionInfo* symbol_info) {
assert_not_null(symbol_info);
auto fn = reinterpret_cast<X64Function*>(symbol_info->function());
void X64Emitter::Call(const hir::Instr* instr, GuestFunction* function) {
assert_not_null(function);
auto fn = static_cast<X64Function*>(function);
// Resolve address to the function to call and store in rax.
if (fn) {
if (fn->machine_code()) {
// TODO(benvanik): is it worth it to do this? It removes the need for
// a ResolveFunction call, but makes the table less useful.
assert_zero(uint64_t(fn->machine_code()) & 0xFFFFFFFF00000000);
@@ -363,7 +363,7 @@ void X64Emitter::Call(const hir::Instr* instr, FunctionInfo* symbol_info) {
// Load the pointer to the indirection table maintained in X64CodeCache.
// The target dword will either contain the address of the generated code
// or a thunk to ResolveAddress.
mov(ebx, symbol_info->address());
mov(ebx, function->address());
mov(eax, dword[ebx]);
}
@@ -418,43 +418,50 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
}
}
uint64_t UndefinedCallExtern(void* raw_context, uint64_t symbol_info_ptr) {
auto symbol_info = reinterpret_cast<FunctionInfo*>(symbol_info_ptr);
XELOGW("undefined extern call to %.8X %s", symbol_info->address(),
symbol_info->name().c_str());
uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) {
auto function = reinterpret_cast<Function*>(function_ptr);
XELOGW("undefined extern call to %.8X %s", function->address(),
function->name().c_str());
return 0;
}
void X64Emitter::CallExtern(const hir::Instr* instr,
const FunctionInfo* symbol_info) {
if (symbol_info->behavior() == FunctionBehavior::kBuiltin &&
symbol_info->builtin_handler()) {
// rcx = context
// rdx = target host function
// r8 = arg0
// r9 = arg1
mov(rdx, reinterpret_cast<uint64_t>(symbol_info->builtin_handler()));
mov(r8, reinterpret_cast<uint64_t>(symbol_info->builtin_arg0()));
mov(r9, reinterpret_cast<uint64_t>(symbol_info->builtin_arg1()));
auto thunk = backend()->guest_to_host_thunk();
mov(rax, reinterpret_cast<uint64_t>(thunk));
call(rax);
ReloadECX();
ReloadEDX();
// rax = host return
} else if (symbol_info->behavior() == FunctionBehavior::kExtern &&
symbol_info->extern_handler()) {
// rcx = context
// rdx = target host function
mov(rdx, reinterpret_cast<uint64_t>(symbol_info->extern_handler()));
mov(r8, qword[rcx + offsetof(cpu::frontend::PPCContext, kernel_state)]);
auto thunk = backend()->guest_to_host_thunk();
mov(rax, reinterpret_cast<uint64_t>(thunk));
call(rax);
ReloadECX();
ReloadEDX();
// rax = host return
} else {
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info));
void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
bool undefined = true;
if (function->behavior() == Function::Behavior::kBuiltin) {
auto builtin_function = static_cast<const BuiltinFunction*>(function);
if (builtin_function->handler()) {
undefined = false;
// rcx = context
// rdx = target host function
// r8 = arg0
// r9 = arg1
mov(rdx, reinterpret_cast<uint64_t>(builtin_function->handler()));
mov(r8, reinterpret_cast<uint64_t>(builtin_function->arg0()));
mov(r9, reinterpret_cast<uint64_t>(builtin_function->arg1()));
auto thunk = backend()->guest_to_host_thunk();
mov(rax, reinterpret_cast<uint64_t>(thunk));
call(rax);
ReloadECX();
ReloadEDX();
// rax = host return
}
} else if (function->behavior() == Function::Behavior::kExtern) {
auto extern_function = static_cast<const GuestFunction*>(function);
if (extern_function->extern_handler()) {
undefined = false;
// rcx = context
// rdx = target host function
mov(rdx, reinterpret_cast<uint64_t>(extern_function->extern_handler()));
mov(r8, qword[rcx + offsetof(cpu::frontend::PPCContext, kernel_state)]);
auto thunk = backend()->guest_to_host_thunk();
mov(rax, reinterpret_cast<uint64_t>(thunk));
call(rax);
ReloadECX();
ReloadEDX();
// rax = host return
}
}
if (undefined) {
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(function));
}
}

View File

@@ -17,7 +17,6 @@
#include "xenia/cpu/hir/hir_builder.h"
#include "xenia/cpu/hir/instr.h"
#include "xenia/cpu/hir/value.h"
#include "xenia/cpu/symbol_info.h"
#include "xenia/debug/function_trace_data.h"
#include "xenia/memory.h"
@@ -27,10 +26,7 @@
namespace xe {
namespace cpu {
class DebugInfo;
class FunctionInfo;
class Processor;
class SymbolInfo;
} // namespace cpu
} // namespace xe
@@ -118,7 +114,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
Processor* processor() const { return processor_; }
X64Backend* backend() const { return backend_; }
bool Emit(FunctionInfo* function_info, hir::HIRBuilder* builder,
bool Emit(GuestFunction* function, hir::HIRBuilder* builder,
uint32_t debug_info_flags, DebugInfo* debug_info,
void*& out_code_address, size_t& out_code_size,
std::vector<SourceMapEntry>& out_source_map);
@@ -163,9 +159,9 @@ class X64Emitter : public Xbyak::CodeGenerator {
void Trap(uint16_t trap_type = 0);
void UnimplementedInstr(const hir::Instr* i);
void Call(const hir::Instr* instr, FunctionInfo* symbol_info);
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 FunctionInfo* symbol_info);
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));
@@ -199,7 +195,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
size_t stack_size() const { return stack_size_; }
protected:
void* Emplace(size_t stack_size, FunctionInfo* function_info = nullptr);
void* Emplace(size_t stack_size, GuestFunction* function = nullptr);
bool Emit(hir::HIRBuilder* builder, size_t& out_stack_size);
void EmitGetCurrentThreadId();
void EmitTraceUserCallReturn();
@@ -218,6 +214,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
DebugInfo* debug_info_ = nullptr;
uint32_t debug_info_flags_ = 0;
debug::FunctionTraceData* trace_data_ = nullptr;
Arena source_map_arena_;
size_t stack_size_ = 0;

View File

@@ -18,8 +18,8 @@ namespace cpu {
namespace backend {
namespace x64 {
X64Function::X64Function(FunctionInfo* symbol_info)
: Function(symbol_info), machine_code_(nullptr), machine_code_length_(0) {}
X64Function::X64Function(Module* module, uint32_t address)
: GuestFunction(module, address) {}
X64Function::~X64Function() {
// machine_code_ is freed by code cache.
@@ -30,14 +30,6 @@ void X64Function::Setup(uint8_t* machine_code, size_t machine_code_length) {
machine_code_length_ = machine_code_length;
}
bool X64Function::AddBreakpointImpl(debug::Breakpoint* breakpoint) {
return false;
}
bool X64Function::RemoveBreakpointImpl(debug::Breakpoint* breakpoint) {
return false;
}
bool X64Function::CallImpl(ThreadState* thread_state, uint32_t return_address) {
auto backend =
reinterpret_cast<X64Backend*>(thread_state->processor()->backend());

View File

@@ -11,7 +11,6 @@
#define XENIA_BACKEND_X64_X64_FUNCTION_H_
#include "xenia/cpu/function.h"
#include "xenia/cpu/symbol_info.h"
#include "xenia/cpu/thread_state.h"
namespace xe {
@@ -19,10 +18,10 @@ namespace cpu {
namespace backend {
namespace x64 {
class X64Function : public Function {
class X64Function : public GuestFunction {
public:
X64Function(FunctionInfo* symbol_info);
virtual ~X64Function();
X64Function(Module* module, uint32_t address);
~X64Function() override;
uint8_t* machine_code() const override { return machine_code_; }
size_t machine_code_length() const override { return machine_code_length_; }
@@ -30,13 +29,11 @@ class X64Function : public Function {
void Setup(uint8_t* machine_code, size_t machine_code_length);
protected:
bool AddBreakpointImpl(debug::Breakpoint* breakpoint) override;
bool RemoveBreakpointImpl(debug::Breakpoint* breakpoint) override;
bool CallImpl(ThreadState* thread_state, uint32_t return_address) override;
private:
uint8_t* machine_code_;
size_t machine_code_length_;
uint8_t* machine_code_ = nullptr;
size_t machine_code_length_ = 0;
};
} // namespace x64

View File

@@ -154,7 +154,7 @@ struct OffsetOp : Op<OffsetOp, KEY_TYPE_O> {
};
struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
FunctionInfo* value;
Function* value;
protected:
template <typename T, KeyType KEY_TYPE>
@@ -162,7 +162,7 @@ struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
template <hir::Opcode OPCODE, typename... Ts>
friend struct I;
bool Load(const Instr::Op& op) {
this->value = op.symbol_info;
this->value = op.symbol;
return true;
}
};
@@ -856,7 +856,8 @@ EMITTER_OPCODE_TABLE(OPCODE_TRAP_TRUE, TRAP_TRUE_I8, TRAP_TRUE_I16,
// ============================================================================
struct CALL : Sequence<CALL, I<OPCODE_CALL, VoidOp, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.Call(i.instr, i.src1.value);
assert_true(i.src1.value->is_guest());
e.Call(i.instr, static_cast<GuestFunction*>(i.src1.value));
}
};
EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
@@ -867,60 +868,66 @@ EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
struct CALL_TRUE_I8
: Sequence<CALL_TRUE_I8, I<OPCODE_CALL_TRUE, VoidOp, I8Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, i.src2.value);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
struct CALL_TRUE_I16
: Sequence<CALL_TRUE_I16, I<OPCODE_CALL_TRUE, VoidOp, I16Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, i.src2.value);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
struct CALL_TRUE_I32
: Sequence<CALL_TRUE_I32, I<OPCODE_CALL_TRUE, VoidOp, I32Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, i.src2.value);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
struct CALL_TRUE_I64
: Sequence<CALL_TRUE_I64, I<OPCODE_CALL_TRUE, VoidOp, I64Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, i.src2.value);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
struct CALL_TRUE_F32
: Sequence<CALL_TRUE_F32, I<OPCODE_CALL_TRUE, VoidOp, F32Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.vptest(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, i.src2.value);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
struct CALL_TRUE_F64
: Sequence<CALL_TRUE_F64, I<OPCODE_CALL_TRUE, VoidOp, F64Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.vptest(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, i.src2.value);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};