Files
Xenia-Canary/src/xenia/cpu/hir/instr.h
2024-03-12 19:19:30 +01:00

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_