"turns out theres a lot of quirks with the div instructions we havent been covering if the denom is 0, we jump to the end and mov eax/rax to dst, which is correct because ppc raises no exceptions for divide by 0 unlike x86 except we don't initialize eax before that jump, so whatever garbage from the previous sequence that has been left in eax/rax is what the result of the instruction will be and then in our constant folding, we don't do the same zero check in Value::Div, so if we constant folded the denom to 0 we will host crash the ppc manual says the result for a division by 0 is undefined, but in reality it seems it is always 0 there are a few posts i saw from googling about it, and tests on my rgh gave me 0, but then another issue came up and that is that we dont check for signed overflow in our division, so we raise an exception if guest code ever does (1<<signbit_pos) / -1 signed overflow in division also produces 0 on ppc the last thing is that if src2 is constant we skip the 0 check for division without checking if its nonzero all weird, likely very rare edge cases, except for maybe the signed overflow division chrispy — Today at 9:51 AM oh yeah, and because the int members of constantvalue are all signed ints, we were actually doing signed division always with constant folding" fixed an earlier mistake by me with the precision of fresx made some optimization disableable implemented vkpkx fixed possible bugs with vsr/vsl constant folding disabled the nice imul code for now, there was a bug with int64 version and i dont have time to check started on multiplication/addition/subtraction/division identities Removed optimized VSL implementation, it's going to have to be rewritten anyway Added ppc_ctx_t to xboxkrnl shim for direct context access started working on KeSaveFloatingPointState, re'ed most of it Exposed some more state/functionality to the kernel for implementing lower level routines like the save/restore ones Add cvar to re-enable incorrect mxcsr behavior if a user doesnt care and wants better cpu performance Stubbed out more impossible sequences, replace mul_hi_i32 with a 64 bit multiply
179 lines
4.7 KiB
C++
179 lines
4.7 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_INSTR_H_
|
|
#define XENIA_CPU_HIR_INSTR_H_
|
|
|
|
#include "xenia/cpu/hir/opcodes.h"
|
|
#include "xenia/cpu/hir/value.h"
|
|
|
|
namespace xe {
|
|
namespace cpu {
|
|
class Function;
|
|
} // namespace cpu
|
|
} // namespace xe
|
|
|
|
namespace xe {
|
|
namespace cpu {
|
|
namespace hir {
|
|
|
|
class Block;
|
|
class Label;
|
|
// todo: better name
|
|
enum MovTunnel {
|
|
MOVTUNNEL_ASSIGNS = 1,
|
|
MOVTUNNEL_MOVZX = 2,
|
|
MOVTUNNEL_MOVSX = 4,
|
|
MOVTUNNEL_TRUNCATE = 8,
|
|
MOVTUNNEL_AND32FF = 16, // tunnel through and with 0xFFFFFFFF
|
|
};
|
|
|
|
class Instr {
|
|
public:
|
|
Block* block;
|
|
Instr* next;
|
|
Instr* prev;
|
|
|
|
const OpcodeInfo* opcode;
|
|
uint16_t flags;
|
|
uint16_t backend_flags; // backends may do whatever they wish with this
|
|
uint32_t ordinal;
|
|
|
|
typedef union {
|
|
Function* symbol;
|
|
Label* label;
|
|
Value* value;
|
|
uint64_t offset;
|
|
} Op;
|
|
|
|
Value* dest;
|
|
union {
|
|
struct {
|
|
Op src1;
|
|
Op src2;
|
|
Op src3;
|
|
};
|
|
Op srcs[3];
|
|
};
|
|
union {
|
|
struct {
|
|
Value::Use* src1_use;
|
|
Value::Use* src2_use;
|
|
Value::Use* src3_use;
|
|
};
|
|
Value::Use* srcs_use[3];
|
|
};
|
|
void set_srcN(Value* value, uint32_t idx);
|
|
void set_src1(Value* value) { set_srcN(value, 0); }
|
|
|
|
void set_src2(Value* value) { set_srcN(value, 1); }
|
|
|
|
void set_src3(Value* value) { set_srcN(value, 2); }
|
|
|
|
void MoveBefore(Instr* other);
|
|
void Replace(const OpcodeInfo* new_opcode, uint16_t new_flags);
|
|
void Remove();
|
|
const OpcodeInfo* GetOpcodeInfo() const { return opcode; }
|
|
// if opcode is null, we have bigger problems
|
|
Opcode GetOpcodeNum() const { return GetOpcodeInfo()->num; }
|
|
|
|
template <typename TPredicate>
|
|
std::pair<Value*, Value*> BinaryValueArrangeByPredicateExclusive(
|
|
TPredicate&& pred) {
|
|
auto src1_value = src1.value;
|
|
auto src2_value = src2.value;
|
|
if (!src1_value || !src2_value) return {nullptr, nullptr};
|
|
|
|
if (!GetOpcodeInfo()) return {nullptr, nullptr}; // impossible!
|
|
|
|
// check if binary opcode taking two values. we dont care if the dest is a
|
|
// value
|
|
|
|
if (!IsOpcodeBinaryValue(GetOpcodeInfo()->signature))
|
|
return {nullptr, nullptr};
|
|
|
|
if (pred(src1_value)) {
|
|
if (pred(src2_value)) {
|
|
return {nullptr, nullptr};
|
|
} else {
|
|
return {src1_value, src2_value};
|
|
}
|
|
} else if (pred(src2_value)) {
|
|
return {src2_value, src1_value};
|
|
} else {
|
|
return {nullptr, nullptr};
|
|
}
|
|
}
|
|
|
|
/*
|
|
if src1 is constant, and src2 is not, return [src1, src2]
|
|
if src2 is constant, and src1 is not, return [src2, src1]
|
|
if neither is constant, return nullptr, nullptr
|
|
if both are constant, return nullptr, nullptr
|
|
*/
|
|
std::pair<Value*, Value*> BinaryValueArrangeAsConstAndVar() {
|
|
return BinaryValueArrangeByPredicateExclusive(
|
|
[](Value* value) { return value->IsConstant(); });
|
|
}
|
|
std::pair<Value*, Value*> BinaryValueArrangeByDefiningOpcode(
|
|
const OpcodeInfo* op_ptr) {
|
|
return BinaryValueArrangeByPredicateExclusive([op_ptr](Value* value) {
|
|
return value->def && value->def->GetOpcodeInfo() == op_ptr;
|
|
});
|
|
}
|
|
|
|
Instr* GetDestDefSkipAssigns();
|
|
Instr* GetDestDefTunnelMovs(unsigned int* tunnel_flags);
|
|
|
|
// returns [def op, constant]
|
|
std::pair<Value*, Value*> BinaryValueArrangeByDefOpAndConstant(
|
|
const OpcodeInfo* op_ptr) {
|
|
auto result = BinaryValueArrangeByDefiningOpcode(op_ptr);
|
|
|
|
if (!result.first) return result;
|
|
if (!result.second->IsConstant()) {
|
|
return {nullptr, nullptr};
|
|
}
|
|
return result;
|
|
}
|
|
/*
|
|
Invokes the provided lambda callback on each operand that is a Value. Callback
|
|
is invoked with Value*, uint32_t index
|
|
*/
|
|
template <typename TCallable>
|
|
void VisitValueOperands(TCallable&& call_for_values) {
|
|
uint32_t signature = GetOpcodeInfo()->signature;
|
|
|
|
OpcodeSignatureType t_dest, t_src1, t_src2, t_src3;
|
|
|
|
UnpackOpcodeSig(signature, t_dest, t_src1, t_src2, t_src3);
|
|
|
|
if (t_src1 == OPCODE_SIG_TYPE_V) {
|
|
call_for_values(src1.value, 0);
|
|
}
|
|
if (t_src2 == OPCODE_SIG_TYPE_V) {
|
|
call_for_values(src2.value, 1);
|
|
}
|
|
if (t_src3 == OPCODE_SIG_TYPE_V) {
|
|
call_for_values(src3.value, 2);
|
|
}
|
|
}
|
|
bool IsFake() const;
|
|
|
|
// gets previous instr, skipping instrs like COMMENT, OPCODE_CONTEXT_BARRIER,
|
|
// OPCODE_SOURCE_OFFSET
|
|
const hir::Instr* GetNonFakePrev() const;
|
|
};
|
|
|
|
} // namespace hir
|
|
} // namespace cpu
|
|
} // namespace xe
|
|
|
|
#endif // XENIA_CPU_HIR_INSTR_H_
|