Proper handling of nans for VMX max/min on x64 (minps/maxps has special behavior depending on the operand order that vmx does not have for vminfp/vmaxfp) Add extremely unintrusive guest code profiler utilizing KUSER_SHARED systemtime. This profiler is disabled on platforms other than windows, and on windows is disabled by default by a cvar Repurpose GUEST_SCRATCH64 stack offset to instead be for storing guest function profile times, define GUEST_SCRATCH as 0 instead, since thats already meant to be a scratch area Fix xenia silently closing on config errors/other fatal errors by setting has_console_attached_'s default to false Add alternative code path for guest clock that uses kusershared systemtime instead of QueryPerformanceCounter. This is way faster and I have tested it and found it to be working, but i have disabled it because i do not know how well it works on wine or on processors other than mine Significantly reduce log spam by setting XELOGAPU and XELOGGPU to be LogLevel::Debug Changed some LOGI to LOGD in places to reduce log spam Mark VdSwap as kHighFrequency, it was spamming up logs Make logging calls less intrusive for the caller by forcing the test of log level inline and moving the format/AppendLogLine stuff to an outlined cold function Add swcache namespace for software cache operations like prefetches, streaming stores and streaming loads. Add XE_MSVC_REORDER_BARRIER for preventing msvc from propagating a value too close to its store or from its load Add xe_unlikely_mutex for locks we know have very little contention add XE_HOST_CACHE_LINE_SIZE and XE_RESTRICT to platform.h Microoptimization: Changed most uses of size_t to ring_size_t in RingBuffer, this reduces the size of the inlined ringbuffer operations slightly by eliminating rex prefixes, depending on register allocation Add BeginPrefetchedRead to ringbuffer, which prefetches the second range if there is one according to the provided PrefetchTag added inline_loadclock cvar, which will directly use the value of the guest clock from clock.cc in jitted guest code. off by default change uses of GUEST_SCRATCH64 to GUEST_SCRATCH Add fast vectorized xenos_half_to_float/xenos_float_to_half (currently resides in x64_seq_vector, move to gpu code maybe at some point) Add fast x64 codegen for PackFloat16_4/UnpackFloat16_4. Same code can be used for Float16_2 in future commit. This should speed up some games that use these functions heavily Remove cvar for toggling old float16 behavior Add VRSAVE register, support mfspr/mtspr vrsave Add cvar for toggling off codegen for trap instructions and set it to true by default. Add specialized methods to CommandProcessor: WriteRegistersFromMem, WriteRegisterRangeFromRing, and WriteOneRegisterFromRing. These reduce the overall cost of WriteRegister Use a fixed size vmem vector for upload ranges, realloc/memsetting on resize in the inner loop of requestranges was showing up on the profiler (the search in requestranges itself needs work) Rename fixed_vmem_vector to better fit xenia's naming convention Only log unknown register writes in WriteRegister if DEBUG :/. We're stuck on MSVC with c++17 so we have no way of influencing the branch ordering for that function without profile guided optimization Remove binding stride assert in shader_translator.cc, triangle told me its leftover ogl stuff Mark xe::FatalError as noreturn If a controller is not connected, delay by 1.1 seconds before checking if it has been reconnected. Asking Xinput about a controller slot that is unused is extremely slow, and XinputGetState/SetState were taking up an enormous amount of time in profiles. this may have caused a bit of input lag Protect accesses to input_system with a lock Add proper handling for user_index>= 4 in XamInputGetState/SetState, properly return zeroed state in GetState Add missing argument to NtQueryVirtualMemory_entry Fixed RtlCompareMemoryUlong_entry, it actually does not care if the source is misaligned, and for length it aligns down Fixed RtlUpperChar and RtlLowerChar, added a table that has their correct return values precomputed
634 lines
18 KiB
C++
634 lines
18 KiB
C++
/**
|
|
******************************************************************************
|
|
* 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_HIR_VALUE_H_
|
|
#define XENIA_CPU_HIR_VALUE_H_
|
|
|
|
#include "xenia/base/arena.h"
|
|
#include "xenia/base/assert.h"
|
|
#include "xenia/base/vec128.h"
|
|
#include "xenia/cpu/backend/machine_info.h"
|
|
#include "xenia/cpu/hir/opcodes.h"
|
|
|
|
namespace xe {
|
|
namespace cpu {
|
|
namespace hir {
|
|
|
|
class Instr;
|
|
|
|
using vec128_t = xe::vec128_t;
|
|
|
|
enum TypeName {
|
|
// Many tables rely on this ordering.
|
|
INT8_TYPE = 0,
|
|
INT16_TYPE = 1,
|
|
INT32_TYPE = 2,
|
|
INT64_TYPE = 3,
|
|
FLOAT32_TYPE = 4,
|
|
FLOAT64_TYPE = 5,
|
|
VEC128_TYPE = 6,
|
|
MAX_TYPENAME,
|
|
};
|
|
|
|
inline size_t GetTypeSize(TypeName type_name) {
|
|
switch (type_name) {
|
|
case INT8_TYPE:
|
|
return 1;
|
|
case INT16_TYPE:
|
|
return 2;
|
|
case INT32_TYPE:
|
|
return 4;
|
|
case INT64_TYPE:
|
|
return 8;
|
|
case FLOAT32_TYPE:
|
|
return 4;
|
|
case FLOAT64_TYPE:
|
|
return 8;
|
|
case VEC128_TYPE:
|
|
return 16;
|
|
default:
|
|
assert_unhandled_case(type_name);
|
|
return 0;
|
|
}
|
|
}
|
|
inline uint64_t GetScalarTypeMask(TypeName type_name) {
|
|
size_t mask_width = GetTypeSize(type_name);
|
|
|
|
if (mask_width == 8) {
|
|
return ~0ULL;
|
|
} else {
|
|
return (1ULL << (mask_width * CHAR_BIT)) - 1;
|
|
}
|
|
}
|
|
static inline uint64_t GetScalarSignbitMask(TypeName type_name) {
|
|
size_t type_width = GetTypeSize(type_name);
|
|
|
|
return 1ULL << ((type_width * CHAR_BIT) - 1);
|
|
}
|
|
|
|
static inline bool IsScalarIntegralType(TypeName type_name) {
|
|
return type_name < FLOAT32_TYPE && type_name >= INT8_TYPE;
|
|
}
|
|
enum ValueFlags {
|
|
VALUE_IS_CONSTANT = (1 << 1),
|
|
VALUE_IS_ALLOCATED = (1 << 2), // Used by backends. Do not set.
|
|
};
|
|
|
|
struct RegAssignment {
|
|
const backend::MachineInfo::RegisterSet* set;
|
|
int32_t index;
|
|
};
|
|
|
|
struct ValueMask {
|
|
uint64_t low; // low 64 bits, usually for scalar values
|
|
uint64_t high; // high 64 bits, only used for vector types
|
|
|
|
ValueMask(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
|
|
|
|
ValueMask operator&(ValueMask other) const {
|
|
return ValueMask{low & other.low, high & other.high};
|
|
}
|
|
ValueMask operator|(ValueMask other) const {
|
|
return ValueMask{low | other.low, high | other.high};
|
|
}
|
|
ValueMask operator^(ValueMask other) const {
|
|
return ValueMask{low ^ other.low, high ^ other.high};
|
|
}
|
|
};
|
|
|
|
class Value {
|
|
public:
|
|
/*
|
|
todo : this should be intrusive and be part of Instr instead.
|
|
*/
|
|
typedef struct Use_s {
|
|
Instr* instr;
|
|
Use_s* prev;
|
|
Use_s* next;
|
|
} Use;
|
|
typedef union {
|
|
int8_t i8;
|
|
uint8_t u8;
|
|
int16_t i16;
|
|
uint16_t u16;
|
|
int32_t i32;
|
|
uint32_t u32;
|
|
int64_t i64;
|
|
uint64_t u64;
|
|
float f32;
|
|
double f64;
|
|
vec128_t v128;
|
|
} ConstantValue;
|
|
|
|
public:
|
|
uint32_t ordinal;
|
|
TypeName type;
|
|
|
|
uint32_t flags;
|
|
|
|
Instr* def;
|
|
Use* use_head;
|
|
// NOTE: for performance reasons this is not maintained during construction.
|
|
Instr* last_use;
|
|
RegAssignment reg;
|
|
union {
|
|
Value* local_slot;
|
|
ConstantValue constant;
|
|
};
|
|
|
|
Use* AddUse(Arena* arena, Instr* instr);
|
|
void RemoveUse(Use* use);
|
|
|
|
void set_zero(TypeName new_type) {
|
|
type = new_type;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.v128.low = constant.v128.high = 0;
|
|
}
|
|
void set_constant(int8_t value) {
|
|
type = INT8_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.i64 = int64_t(value);
|
|
}
|
|
void set_constant(uint8_t value) {
|
|
type = INT8_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.i64 = uint64_t(value);
|
|
}
|
|
void set_constant(int16_t value) {
|
|
type = INT16_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.i64 = int64_t(value);
|
|
}
|
|
void set_constant(uint16_t value) {
|
|
type = INT16_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.i64 = uint64_t(value);
|
|
}
|
|
void set_constant(int32_t value) {
|
|
type = INT32_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.i64 = int64_t(value);
|
|
}
|
|
void set_constant(uint32_t value) {
|
|
type = INT32_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.i64 = uint64_t(value);
|
|
}
|
|
void set_constant(int64_t value) {
|
|
type = INT64_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.i64 = value;
|
|
}
|
|
void set_constant(uint64_t value) {
|
|
type = INT64_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.i64 = value;
|
|
}
|
|
void set_constant(float value) {
|
|
type = FLOAT32_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.f32 = value;
|
|
}
|
|
void set_constant(double value) {
|
|
type = FLOAT64_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.f64 = value;
|
|
}
|
|
void set_constant(const vec128_t& value) {
|
|
type = VEC128_TYPE;
|
|
flags |= VALUE_IS_CONSTANT;
|
|
constant.v128 = value;
|
|
}
|
|
void set_from(const Value* other) {
|
|
assert_true(other->IsConstant());
|
|
type = other->type;
|
|
flags = other->flags;
|
|
constant.v128 = other->constant.v128;
|
|
}
|
|
bool HasLocalSlot() const {
|
|
return !(flags & VALUE_IS_CONSTANT) && local_slot;
|
|
}
|
|
void SetLocalSlot(Value* lslot) {
|
|
assert(!(flags & VALUE_IS_CONSTANT));
|
|
local_slot = lslot;
|
|
}
|
|
|
|
Value* GetLocalSlot() {
|
|
return (flags & VALUE_IS_CONSTANT) ? nullptr : local_slot;
|
|
}
|
|
const Value* GetLocalSlot() const {
|
|
return (flags & VALUE_IS_CONSTANT) ? nullptr : local_slot;
|
|
}
|
|
inline bool IsConstant() const { return !!(flags & VALUE_IS_CONSTANT); }
|
|
|
|
inline bool IsEqual(const Value* other) const {
|
|
if (this == other) {
|
|
return true;
|
|
} else if ((this->flags & other->flags) & VALUE_IS_CONSTANT) {
|
|
return this->IsConstantEQ(other);
|
|
}
|
|
return false;
|
|
}
|
|
bool IsConstantTrue() const {
|
|
if (type == VEC128_TYPE) {
|
|
assert_always();
|
|
}
|
|
if (flags & VALUE_IS_CONSTANT) {
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return !!constant.i8;
|
|
case INT16_TYPE:
|
|
return !!constant.i16;
|
|
case INT32_TYPE:
|
|
return !!constant.i32;
|
|
case INT64_TYPE:
|
|
return !!constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return !!constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return !!constant.f64;
|
|
case VEC128_TYPE:
|
|
return constant.v128.low || constant.v128.high;
|
|
default:
|
|
assert_unhandled_case(type);
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantFalse() const {
|
|
if (flags & VALUE_IS_CONSTANT) {
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return !constant.i8;
|
|
case INT16_TYPE:
|
|
return !constant.i16;
|
|
case INT32_TYPE:
|
|
return !constant.i32;
|
|
case INT64_TYPE:
|
|
return !constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return !constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return !constant.f64;
|
|
case VEC128_TYPE:
|
|
return !(constant.v128.low || constant.v128.high);
|
|
default:
|
|
assert_unhandled_case(type);
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantZero() const {
|
|
if (flags & VALUE_IS_CONSTANT) {
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return !constant.i8;
|
|
case INT16_TYPE:
|
|
return !constant.i16;
|
|
case INT32_TYPE:
|
|
return !constant.i32;
|
|
case INT64_TYPE:
|
|
return !constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return !constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return !constant.f64;
|
|
case VEC128_TYPE:
|
|
return !constant.v128.low && !constant.v128.high;
|
|
default:
|
|
assert_unhandled_case(type);
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantOne() const {
|
|
if (flags & VALUE_IS_CONSTANT) {
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return constant.i8 == 1;
|
|
case INT16_TYPE:
|
|
return constant.i16 == 1;
|
|
case INT32_TYPE:
|
|
return constant.i32 == 1;
|
|
case INT64_TYPE:
|
|
return constant.i64 == 1;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 == 1.f;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 == 1.0;
|
|
default:
|
|
assert_unhandled_case(type);
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantEQ(const Value* other) const {
|
|
if (type == VEC128_TYPE) {
|
|
assert_always();
|
|
}
|
|
if ((flags & VALUE_IS_CONSTANT) && (other->flags & VALUE_IS_CONSTANT)) {
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return constant.i8 == other->constant.i8;
|
|
case INT16_TYPE:
|
|
return constant.i16 == other->constant.i16;
|
|
case INT32_TYPE:
|
|
return constant.i32 == other->constant.i32;
|
|
case INT64_TYPE:
|
|
return constant.i64 == other->constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 == other->constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 == other->constant.f64;
|
|
default:
|
|
assert_unhandled_case(type);
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantNE(Value* other) const {
|
|
if (type == VEC128_TYPE) {
|
|
assert_always();
|
|
}
|
|
if ((flags & VALUE_IS_CONSTANT) && (other->flags & VALUE_IS_CONSTANT)) {
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return constant.i8 != other->constant.i8;
|
|
case INT16_TYPE:
|
|
return constant.i16 != other->constant.i16;
|
|
case INT32_TYPE:
|
|
return constant.i32 != other->constant.i32;
|
|
case INT64_TYPE:
|
|
return constant.i64 != other->constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 != other->constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 != other->constant.f64;
|
|
default:
|
|
assert_unhandled_case(type);
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantSLT(Value* other) const {
|
|
assert_true(flags & VALUE_IS_CONSTANT && other->flags & VALUE_IS_CONSTANT);
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return constant.i8 < other->constant.i8;
|
|
case INT16_TYPE:
|
|
return constant.i16 < other->constant.i16;
|
|
case INT32_TYPE:
|
|
return constant.i32 < other->constant.i32;
|
|
case INT64_TYPE:
|
|
return constant.i64 < other->constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 < other->constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 < other->constant.f64;
|
|
default:
|
|
assert_always();
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantSLE(Value* other) const {
|
|
assert_true(flags & VALUE_IS_CONSTANT && other->flags & VALUE_IS_CONSTANT);
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return constant.i8 <= other->constant.i8;
|
|
case INT16_TYPE:
|
|
return constant.i16 <= other->constant.i16;
|
|
case INT32_TYPE:
|
|
return constant.i32 <= other->constant.i32;
|
|
case INT64_TYPE:
|
|
return constant.i64 <= other->constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 <= other->constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 <= other->constant.f64;
|
|
default:
|
|
assert_always();
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantSGT(Value* other) const {
|
|
assert_true(flags & VALUE_IS_CONSTANT && other->flags & VALUE_IS_CONSTANT);
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return constant.i8 > other->constant.i8;
|
|
case INT16_TYPE:
|
|
return constant.i16 > other->constant.i16;
|
|
case INT32_TYPE:
|
|
return constant.i32 > other->constant.i32;
|
|
case INT64_TYPE:
|
|
return constant.i64 > other->constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 > other->constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 > other->constant.f64;
|
|
default:
|
|
assert_always();
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantSGE(Value* other) const {
|
|
assert_true(flags & VALUE_IS_CONSTANT && other->flags & VALUE_IS_CONSTANT);
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return constant.i8 >= other->constant.i8;
|
|
case INT16_TYPE:
|
|
return constant.i16 >= other->constant.i16;
|
|
case INT32_TYPE:
|
|
return constant.i32 >= other->constant.i32;
|
|
case INT64_TYPE:
|
|
return constant.i64 >= other->constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 >= other->constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 >= other->constant.f64;
|
|
default:
|
|
assert_always();
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantULT(Value* other) const {
|
|
assert_true(flags & VALUE_IS_CONSTANT && other->flags & VALUE_IS_CONSTANT);
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return (uint8_t)constant.i8 < (uint8_t)other->constant.i8;
|
|
case INT16_TYPE:
|
|
return (uint16_t)constant.i16 < (uint16_t)other->constant.i16;
|
|
case INT32_TYPE:
|
|
return (uint32_t)constant.i32 < (uint32_t)other->constant.i32;
|
|
case INT64_TYPE:
|
|
return (uint64_t)constant.i64 < (uint64_t)other->constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 < other->constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 < other->constant.f64;
|
|
default:
|
|
assert_always();
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantULE(Value* other) const {
|
|
assert_true(flags & VALUE_IS_CONSTANT && other->flags & VALUE_IS_CONSTANT);
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return (uint8_t)constant.i8 <= (uint8_t)other->constant.i8;
|
|
case INT16_TYPE:
|
|
return (uint16_t)constant.i16 <= (uint16_t)other->constant.i16;
|
|
case INT32_TYPE:
|
|
return (uint32_t)constant.i32 <= (uint32_t)other->constant.i32;
|
|
case INT64_TYPE:
|
|
return (uint64_t)constant.i64 <= (uint64_t)other->constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 <= other->constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 <= other->constant.f64;
|
|
default:
|
|
assert_always();
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantUGT(Value* other) const {
|
|
assert_true(flags & VALUE_IS_CONSTANT && other->flags & VALUE_IS_CONSTANT);
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return (uint8_t)constant.i8 > (uint8_t)other->constant.i8;
|
|
case INT16_TYPE:
|
|
return (uint16_t)constant.i16 > (uint16_t)other->constant.i16;
|
|
case INT32_TYPE:
|
|
return (uint32_t)constant.i32 > (uint32_t)other->constant.i32;
|
|
case INT64_TYPE:
|
|
return (uint64_t)constant.i64 > (uint64_t)other->constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 > other->constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 > other->constant.f64;
|
|
default:
|
|
assert_always();
|
|
return false;
|
|
}
|
|
}
|
|
bool IsConstantUGE(Value* other) const {
|
|
assert_true(flags & VALUE_IS_CONSTANT && other->flags & VALUE_IS_CONSTANT);
|
|
switch (type) {
|
|
case INT8_TYPE:
|
|
return (uint8_t)constant.i8 >= (uint8_t)other->constant.i8;
|
|
case INT16_TYPE:
|
|
return (uint16_t)constant.i16 >= (uint16_t)other->constant.i16;
|
|
case INT32_TYPE:
|
|
return (uint32_t)constant.i32 >= (uint32_t)other->constant.i32;
|
|
case INT64_TYPE:
|
|
return (uint64_t)constant.i64 >= (uint64_t)other->constant.i64;
|
|
case FLOAT32_TYPE:
|
|
return constant.f32 >= other->constant.f32;
|
|
case FLOAT64_TYPE:
|
|
return constant.f64 >= other->constant.f64;
|
|
default:
|
|
assert_always();
|
|
return false;
|
|
}
|
|
}
|
|
uint32_t AsUint32();
|
|
uint64_t AsUint64();
|
|
|
|
void Cast(TypeName target_type);
|
|
void ZeroExtend(TypeName target_type);
|
|
void SignExtend(TypeName target_type);
|
|
void Truncate(TypeName target_type);
|
|
void Convert(TypeName target_type, RoundMode round_mode);
|
|
void Round(RoundMode round_mode);
|
|
bool Add(Value* other);
|
|
bool Sub(Value* other);
|
|
void Mul(Value* other);
|
|
void MulHi(Value* other, bool is_unsigned);
|
|
void Div(Value* other, bool is_unsigned);
|
|
void Max(Value* other);
|
|
|
|
void Neg();
|
|
void Abs();
|
|
void Sqrt();
|
|
void RSqrt();
|
|
void Recip();
|
|
void And(Value* other);
|
|
void Or(Value* other);
|
|
void Xor(Value* other);
|
|
void Not();
|
|
void AndNot(Value* other);
|
|
void Shl(Value* other);
|
|
void Shr(Value* other);
|
|
void Sha(Value* other);
|
|
void RotateLeft(Value* other);
|
|
void Insert(Value* index, Value* part, TypeName type);
|
|
void Extract(Value* vec, Value* index);
|
|
void Permute(Value* src1, Value* src2, TypeName type);
|
|
void Swizzle(uint32_t mask, TypeName type);
|
|
void Select(Value* other, Value* ctrl);
|
|
void Splat(Value* other);
|
|
void VectorCompareEQ(Value* other, TypeName type);
|
|
void VectorCompareSGT(Value* other, TypeName type);
|
|
void VectorCompareSGE(Value* other, TypeName type);
|
|
void VectorCompareUGT(Value* other, TypeName type);
|
|
void VectorCompareUGE(Value* other, TypeName type);
|
|
void VectorConvertI2F(Value* other, bool is_unsigned);
|
|
void VectorConvertF2I(Value* other, bool is_unsigned);
|
|
void VectorShl(Value* other, TypeName type);
|
|
void VectorShr(Value* other, TypeName type);
|
|
void VectorRol(Value* other, TypeName type);
|
|
void VectorAdd(Value* other, TypeName type, bool is_unsigned, bool saturate);
|
|
void VectorSub(Value* other, TypeName type, bool is_unsigned, bool saturate);
|
|
void DotProduct3(Value* other);
|
|
void DotProduct4(Value* other);
|
|
void VectorAverage(Value* other, TypeName type, bool is_unsigned,
|
|
bool saturate);
|
|
void ByteSwap();
|
|
void DenormalFlush();
|
|
void CountLeadingZeros(const Value* other);
|
|
bool Compare(Opcode opcode, Value* other);
|
|
hir::Instr* GetDefSkipAssigns();
|
|
// tunnel_flags is updated to the kinds we actually traversed
|
|
hir::Instr* GetDefTunnelMovs(unsigned int* tunnel_flags);
|
|
|
|
// does the value only have one instr that uses it?
|
|
bool HasSingleUse() const;
|
|
// returns true if every single use is as an operand to a single instruction
|
|
// (add var2, var1, var1)
|
|
bool AllUsesByOneInsn() const;
|
|
// the maybe is here because this includes vec128, which is untyped data that
|
|
// can be treated as float or int depending on the context
|
|
bool MaybeFloaty() const {
|
|
return type == FLOAT32_TYPE || type == FLOAT64_TYPE || type == VEC128_TYPE;
|
|
}
|
|
|
|
private:
|
|
static bool CompareInt8(Opcode opcode, Value* a, Value* b);
|
|
static bool CompareInt16(Opcode opcode, Value* a, Value* b);
|
|
static bool CompareInt32(Opcode opcode, Value* a, Value* b);
|
|
static bool CompareInt64(Opcode opcode, Value* a, Value* b);
|
|
};
|
|
|
|
} // namespace hir
|
|
} // namespace cpu
|
|
} // namespace xe
|
|
|
|
#endif // XENIA_CPU_HIR_VALUE_H_
|