189 lines
5.2 KiB
C++
189 lines
5.2 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 UnlinkAndNOP();
|
|
// chrispy: wanted to change this one to Remove, but i changed Remove's name
|
|
// to UnlinkAndNOP, so if changes happened in master that we wanted to port
|
|
// over, and those changes used Remove, then we would have a lot of issues
|
|
// that the cause of would be difficult to track ^todo: rework this comment,
|
|
// im frazzled
|
|
void Deallocate();
|
|
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;
|
|
|
|
uint32_t GuestAddressFor() const;
|
|
|
|
bool AllScalarIntegral(); // dest and all srcs are scalar integral
|
|
};
|
|
|
|
} // namespace hir
|
|
} // namespace cpu
|
|
} // namespace xe
|
|
|
|
#endif // XENIA_CPU_HIR_INSTR_H_
|