mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 18:00:17 +02:00
I utilize this functionality quite heavily when debugging and I need bread crumbs spread around. Instead of reimplementing it a dozen times, just have it upstreamed.
458 lines
15 KiB
C++
458 lines
15 KiB
C++
// SPDX-License-Identifier: MIT
|
|
/*
|
|
$info$
|
|
meta: ir|dumper ~ IR -> Text
|
|
tags: ir|dumper
|
|
$end_info$
|
|
*/
|
|
|
|
#include "Interface/IR/IntrusiveIRList.h"
|
|
#include "Interface/IR/RegisterAllocationData.h"
|
|
|
|
#include <FEXCore/IR/IR.h>
|
|
#include <FEXCore/fextl/fmt.h>
|
|
#include <FEXCore/fextl/sstream.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <ostream>
|
|
#include <stdint.h>
|
|
#include <string_view>
|
|
#include <iomanip>
|
|
|
|
namespace FEXCore::IR {
|
|
#define IROP_GETNAME_IMPL
|
|
#define IROP_GETRAARGS_IMPL
|
|
#define IROP_REG_CLASSES_IMPL
|
|
#define IROP_HASSIDEEFFECTS_IMPL
|
|
#define IROP_SIZES_IMPL
|
|
#define IROP_GETHASDEST_IMPL
|
|
|
|
#include <FEXCore/IR/IRDefines.inc>
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, const SHA256Sum& Arg) {
|
|
*out << fextl::fmt::format("sha256:{:02x}", fmt::join(Arg.data, ""));
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, uint64_t Arg) {
|
|
*out << fextl::fmt::format("#{:#x}", Arg);
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, const char* const Arg) {
|
|
*out << fextl::fmt::format("'{}'", Arg);
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, CondClass Arg) {
|
|
if (Arg == CondClass::AL) {
|
|
*out << "ALWAYS";
|
|
return;
|
|
}
|
|
|
|
static constexpr std::array<std::string_view, 22> CondNames = {"EQ", "NEQ", "UGE", "ULT", "MI", "PL", "VS", "VC",
|
|
"UGT", "ULE", "SGE", "SLT", "SGT", "SLE", "TSTZ", "TSTNZ",
|
|
"FLU", "FGE", "FLEU", "FGT", "FU", "FNU"};
|
|
|
|
*out << CondNames[FEXCore::ToUnderlying(Arg)];
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, MemOffsetType Arg) {
|
|
static constexpr std::array<std::string_view, 3> Names = {
|
|
"SXTX",
|
|
"UXTW",
|
|
"SXTW",
|
|
};
|
|
|
|
*out << Names[FEXCore::ToUnderlying(Arg)];
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, RegClass Arg) {
|
|
*out << [Arg] {
|
|
switch (Arg) {
|
|
case RegClass::Invalid: return "Invalid";
|
|
case RegClass::GPR: return "GPR";
|
|
case RegClass::GPRFixed: return "GPRFixed";
|
|
case RegClass::FPR: return "FPR";
|
|
case RegClass::FPRFixed: return "FPRFixed";
|
|
case RegClass::Complex: return "Complex";
|
|
}
|
|
return "<Unknown RegClass Type>";
|
|
}();
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView* IR, OrderedNodeWrapper Arg) {
|
|
if (Arg.IsImmediate()) {
|
|
auto PhyReg = PhysicalRegister(Arg);
|
|
|
|
switch (PhyReg.AsRegClass()) {
|
|
case RegClass::GPR: *out << "r"; break;
|
|
case RegClass::GPRFixed: *out << "R"; break;
|
|
case RegClass::FPR: *out << "v"; break;
|
|
case RegClass::FPRFixed: *out << "V"; break;
|
|
case RegClass::Complex: *out << "c"; break;
|
|
case RegClass::Invalid: *out << "invalid"; break;
|
|
default: *out << "unknown"; break;
|
|
}
|
|
|
|
if (PhyReg.AsRegClass() != RegClass::Invalid) {
|
|
*out << std::dec << uint32_t(PhyReg.Reg);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
auto [CodeNode, IROp] = IR->at(Arg)();
|
|
const auto ArgID = Arg.ID();
|
|
|
|
if (ArgID.IsInvalid()) {
|
|
*out << "%Invalid";
|
|
} else {
|
|
*out << "%" << std::dec << ArgID;
|
|
}
|
|
|
|
if (GetHasDest(IROp->Op)) {
|
|
auto ElementSize = IROp->ElementSize;
|
|
uint32_t NumElements = 0;
|
|
if (IROp->ElementSize == OpSize::iUnsized) {
|
|
ElementSize = IROp->Size;
|
|
}
|
|
|
|
if (ElementSize != OpSize::iUnsized) {
|
|
NumElements = IR::NumElements(IROp->Size, ElementSize);
|
|
}
|
|
|
|
*out << " i" << std::dec << IR::OpSizeAsBits(ElementSize);
|
|
|
|
if (NumElements > 1) {
|
|
*out << "v" << std::dec << NumElements;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, FenceType Arg) {
|
|
*out << [Arg] {
|
|
switch (Arg) {
|
|
case FenceType::Load: return "Loads";
|
|
case FenceType::Store: return "Stores";
|
|
case FenceType::LoadStore: return "LoadStores";
|
|
case FenceType::Inst: return "Instruction";
|
|
}
|
|
return "<Unknown Fence Type>";
|
|
}();
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, RoundMode Arg) {
|
|
*out << [Arg] {
|
|
switch (Arg) {
|
|
case RoundMode::Nearest: return "Nearest";
|
|
case RoundMode::NegInfinity: return "-Inf";
|
|
case RoundMode::PosInfinity: return "+Inf";
|
|
case RoundMode::TowardsZero: return "Towards Zero";
|
|
case RoundMode::Host: return "Host";
|
|
}
|
|
return "<Unknown Round Type>";
|
|
}();
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, ConstPad Arg) {
|
|
*out << [Arg] {
|
|
switch (Arg) {
|
|
case ConstPad::NoPad: return "NoPad";
|
|
case ConstPad::DoPad: return "DoPad";
|
|
case ConstPad::AutoPad: return "AutoPad";
|
|
}
|
|
return "<Unknown ConstPad Type>";
|
|
}();
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, NamedVectorConstant Arg) {
|
|
*out << [Arg] {
|
|
// clang-format off
|
|
switch (Arg) {
|
|
case NamedVectorConstant::NAMED_VECTOR_INCREMENTAL_U16_INDEX:
|
|
return "u16_incremental_index";
|
|
case NamedVectorConstant::NAMED_VECTOR_INCREMENTAL_U16_INDEX_UPPER:
|
|
return "u16_incremental_index_upper";
|
|
case NamedVectorConstant::NAMED_VECTOR_PADDSUBPS_INVERT:
|
|
return "addsubps_invert";
|
|
case NamedVectorConstant::NAMED_VECTOR_PADDSUBPS_INVERT_UPPER:
|
|
return "addsubps_invert_upper";
|
|
case NamedVectorConstant::NAMED_VECTOR_PADDSUBPD_INVERT:
|
|
return "addsubpd_invert";
|
|
case NamedVectorConstant::NAMED_VECTOR_PADDSUBPD_INVERT_UPPER:
|
|
return "addsubpd_invert_upper";
|
|
case NamedVectorConstant::NAMED_VECTOR_PSUBADDPS_INVERT:
|
|
return "subaddps_invert";
|
|
case NamedVectorConstant::NAMED_VECTOR_PSUBADDPS_INVERT_UPPER:
|
|
return "subaddps_invert_upper";
|
|
case NamedVectorConstant::NAMED_VECTOR_PSUBADDPD_INVERT:
|
|
return "subaddpd_invert";
|
|
case NamedVectorConstant::NAMED_VECTOR_PSUBADDPD_INVERT_UPPER:
|
|
return "subaddpd_invert_upper";
|
|
case NamedVectorConstant::NAMED_VECTOR_MOVMSKPS_SHIFT:
|
|
return "movmskps_shift";
|
|
case NamedVectorConstant::NAMED_VECTOR_AESKEYGENASSIST_SWIZZLE:
|
|
return "aeskeygenassist_swizzle";
|
|
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_0110B:
|
|
return "blendps_0110b";
|
|
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_0111B:
|
|
return "blendps_0111b";
|
|
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_1001B:
|
|
return "blendps_1001b";
|
|
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_1011B:
|
|
return "blendps_1011b";
|
|
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_1101B:
|
|
return "blendps_1101b";
|
|
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_1110B:
|
|
return "blendps_1110b";
|
|
case NamedVectorConstant::NAMED_VECTOR_MOVMASKB:
|
|
return "movmaskb";
|
|
case NamedVectorConstant::NAMED_VECTOR_MOVMASKB_UPPER:
|
|
return "movmaskb_upper";
|
|
case NamedVectorConstant::NAMED_VECTOR_ZERO:
|
|
return "vectorzero";
|
|
case NamedVectorConstant::NAMED_VECTOR_X87_ONE:
|
|
return "x87_1_0";
|
|
case NamedVectorConstant::NAMED_VECTOR_X87_LOG2_10:
|
|
return "x87_log2_10";
|
|
case NamedVectorConstant::NAMED_VECTOR_X87_LOG2_E:
|
|
return "x87_log2_e";
|
|
case NamedVectorConstant::NAMED_VECTOR_X87_PI:
|
|
return "x87_pi";
|
|
case NamedVectorConstant::NAMED_VECTOR_X87_LOG10_2:
|
|
return "x87_log10_2";
|
|
case NamedVectorConstant::NAMED_VECTOR_X87_LOG_2:
|
|
return "x87_log2";
|
|
case NamedVectorConstant::NAMED_VECTOR_CVTMAX_F32_I32:
|
|
return "cvtmax_f32_i32";
|
|
case NamedVectorConstant::NAMED_VECTOR_CVTMAX_F32_I32_UPPER:
|
|
return "cvtmax_f32_i32_upper";
|
|
case NamedVectorConstant::NAMED_VECTOR_CVTMAX_F32_I64:
|
|
return "cvtmax_f32_i64";
|
|
case NamedVectorConstant::NAMED_VECTOR_CVTMAX_F64_I32:
|
|
return "cvtmax_f64_i32";
|
|
case NamedVectorConstant::NAMED_VECTOR_CVTMAX_F64_I32_UPPER:
|
|
return "cvtmax_f64_i32_upper";
|
|
case NamedVectorConstant::NAMED_VECTOR_CVTMAX_F64_I64:
|
|
return "cvtmax_f64_i64";
|
|
case NamedVectorConstant::NAMED_VECTOR_CVTMAX_I32:
|
|
return "cvtmax_i32";
|
|
case NamedVectorConstant::NAMED_VECTOR_CVTMAX_I64:
|
|
return "cvtmax_i64";
|
|
case NamedVectorConstant::NAMED_VECTOR_F80_SIGN_MASK:
|
|
return "f80_sign_mask";
|
|
case NamedVectorConstant::NAMED_VECTOR_SHA1RNDS_K0:
|
|
return "sha1rnds_k0";
|
|
case NamedVectorConstant::NAMED_VECTOR_SHA1RNDS_K1:
|
|
return "sha1rnds_k1";
|
|
case NamedVectorConstant::NAMED_VECTOR_SHA1RNDS_K2:
|
|
return "sha1rnds_k2";
|
|
case NamedVectorConstant::NAMED_VECTOR_SHA1RNDS_K3:
|
|
return "sha1rnds_k3";
|
|
case NamedVectorConstant::NAMED_VECTOR_MAX:
|
|
return "<Programming Error: Printing MAX value>";
|
|
}
|
|
return "<Unknown Named Vector Constant>";
|
|
// clang-format on
|
|
}();
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, IndexNamedVectorConstant Arg) {
|
|
*out << [Arg] {
|
|
// clang-format off
|
|
switch (Arg) {
|
|
case IndexNamedVectorConstant::INDEXED_NAMED_VECTOR_PSHUFLW:
|
|
return "pshuflw";
|
|
case IndexNamedVectorConstant::INDEXED_NAMED_VECTOR_PSHUFHW:
|
|
return "pshufhw";
|
|
case IndexNamedVectorConstant::INDEXED_NAMED_VECTOR_PSHUFD:
|
|
return "pshufd";
|
|
case IndexNamedVectorConstant::INDEXED_NAMED_VECTOR_SHUFPS:
|
|
return "shufps";
|
|
case IndexNamedVectorConstant::INDEXED_NAMED_VECTOR_DPPS_MASK:
|
|
return "dpps_mask";
|
|
case IndexNamedVectorConstant::INDEXED_NAMED_VECTOR_DPPD_MASK:
|
|
return "dppd_mask";
|
|
case IndexNamedVectorConstant::INDEXED_NAMED_VECTOR_PBLENDW:
|
|
return "pblendw";
|
|
case INDEXED_NAMED_VECTOR_MAX:
|
|
return "<Programming Error: Printing MAX value>";
|
|
}
|
|
return "<Unknown Indexed Named Vector Constant>";
|
|
// clang-format on
|
|
}();
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, OpSize Arg) {
|
|
*out << [Arg] {
|
|
switch (Arg) {
|
|
case OpSize::iUnsized: return "Unsized";
|
|
case OpSize::i8Bit: return "i8";
|
|
case OpSize::i16Bit: return "i16";
|
|
case OpSize::i32Bit: return "i32";
|
|
case OpSize::i64Bit: return "i64";
|
|
case OpSize::f80Bit: return "f80";
|
|
case OpSize::i128Bit: return "i128";
|
|
case OpSize::i256Bit: return "i256";
|
|
case OpSize::iInvalid: return "Invalid";
|
|
}
|
|
return "<Unknown OpSize Type>";
|
|
}();
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, FloatCompareOp Arg) {
|
|
*out << [Arg] {
|
|
switch (Arg) {
|
|
case FloatCompareOp::EQ: return "FEQ";
|
|
case FloatCompareOp::LT: return "FLT";
|
|
case FloatCompareOp::LE: return "FLE";
|
|
case FloatCompareOp::UNO: return "UNO";
|
|
case FloatCompareOp::NEQ: return "NEQ";
|
|
case FloatCompareOp::ORD: return "ORD";
|
|
}
|
|
return "<Unknown FloatCompareOp Type>";
|
|
}();
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::BreakDefinition Arg) {
|
|
*out << "{" << Arg.ErrorRegister << ".";
|
|
*out << static_cast<uint32_t>(Arg.Signal) << ".";
|
|
*out << static_cast<uint32_t>(Arg.TrapNumber) << ".";
|
|
*out << static_cast<uint32_t>(Arg.si_code) << "}";
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, ShiftType Arg) {
|
|
*out << [Arg] {
|
|
switch (Arg) {
|
|
case ShiftType::LSL: return "LSL";
|
|
case ShiftType::LSR: return "LSR";
|
|
case ShiftType::ASR: return "ASR";
|
|
case ShiftType::ROR: return "ROR";
|
|
}
|
|
return "<Unknown Shift Type>";
|
|
}();
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, BranchHint Arg) {
|
|
*out << [Arg] {
|
|
switch (Arg) {
|
|
case BranchHint::None: return "None";
|
|
case BranchHint::Call: return "Call";
|
|
case BranchHint::Return: return "Return";
|
|
case BranchHint::CheckTF: return "CheckTF";
|
|
}
|
|
return "<Unknown Branch Hint>";
|
|
}();
|
|
}
|
|
|
|
static void PrintArg(fextl::stringstream* out, const IRListView*, const std::array<uint8_t, 0x10>& Arg) {
|
|
*out << fextl::fmt::format("{:02x}", fmt::join(Arg, ""));
|
|
}
|
|
|
|
void Dump(fextl::stringstream* out, const IRListView* IR) {
|
|
auto HeaderOp = IR->GetHeader();
|
|
|
|
int8_t CurrentIndent = 0;
|
|
auto AddIndent = [&out, &CurrentIndent]() {
|
|
for (uint8_t i = 0; i < CurrentIndent; ++i) {
|
|
*out << "\t";
|
|
}
|
|
};
|
|
|
|
++CurrentIndent;
|
|
AddIndent();
|
|
*out << fextl::fmt::format("(%0) IRHeader %{}, #{:#x}, #{}, #{}\n", HeaderOp->Blocks.ID(), +HeaderOp->OriginalRIP, +HeaderOp->BlockCount,
|
|
+HeaderOp->NumHostInstructions);
|
|
|
|
for (auto [BlockNode, BlockHeader] : IR->GetBlocks()) {
|
|
{
|
|
auto BlockIROp = BlockHeader->C<FEXCore::IR::IROp_CodeBlock>();
|
|
|
|
AddIndent();
|
|
*out << "(%" << IR->GetID(BlockNode) << ") " << "CodeBlock ";
|
|
|
|
*out << "%" << BlockIROp->Begin.ID() << ", ";
|
|
*out << "%" << BlockIROp->Last.ID() << std::endl;
|
|
}
|
|
|
|
++CurrentIndent;
|
|
for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) {
|
|
const auto ID = IR->GetID(CodeNode);
|
|
const auto Name = FEXCore::IR::GetName(IROp->Op);
|
|
|
|
{
|
|
AddIndent();
|
|
if (GetHasDest(IROp->Op)) {
|
|
|
|
auto ElementSize = IROp->ElementSize;
|
|
uint8_t NumElements = 0;
|
|
if (IROp->ElementSize != OpSize::iUnsized) {
|
|
ElementSize = IROp->Size;
|
|
}
|
|
|
|
if (ElementSize != OpSize::iUnsized) {
|
|
NumElements = IR::NumElements(IROp->Size, ElementSize);
|
|
}
|
|
|
|
*out << "%" << std::dec << ID;
|
|
|
|
auto PhyReg = PhysicalRegister(CodeNode);
|
|
if (!PhyReg.IsInvalid()) {
|
|
switch (PhyReg.AsRegClass()) {
|
|
case RegClass::GPR: *out << "(r"; break;
|
|
case RegClass::GPRFixed: *out << "(R"; break;
|
|
case RegClass::FPR: *out << "(v"; break;
|
|
case RegClass::FPRFixed: *out << "(V"; break;
|
|
case RegClass::Complex: *out << "(complex"; break;
|
|
case RegClass::Invalid: *out << "(invalid"; break;
|
|
default: *out << "(unknown"; break;
|
|
}
|
|
if (PhyReg.AsRegClass() != RegClass::Invalid) {
|
|
*out << std::dec << uint32_t(PhyReg.Reg) << ")";
|
|
} else {
|
|
*out << ")";
|
|
}
|
|
}
|
|
|
|
*out << " i" << std::dec << IR::OpSizeAsBits(ElementSize);
|
|
|
|
if (NumElements > 1) {
|
|
*out << "v" << std::dec << NumElements;
|
|
}
|
|
|
|
*out << " = ";
|
|
} else {
|
|
|
|
auto ElementSize = IROp->ElementSize;
|
|
if (IROp->ElementSize == OpSize::iUnsized) {
|
|
ElementSize = IROp->Size;
|
|
}
|
|
uint32_t NumElements = 0;
|
|
if (ElementSize != OpSize::iUnsized) {
|
|
NumElements = IR::NumElements(IROp->Size, ElementSize);
|
|
}
|
|
|
|
*out << "(%" << std::dec << ID << ' ';
|
|
*out << 'i' << std::dec << IR::OpSizeAsBits(ElementSize);
|
|
if (NumElements > 1) {
|
|
*out << 'v' << std::dec << NumElements;
|
|
}
|
|
*out << ") ";
|
|
}
|
|
*out << Name;
|
|
|
|
#define IROP_ARGPRINTER_HELPER
|
|
#include <FEXCore/IR/IRDefines.inc>
|
|
default: *out << "<Unknown Args>"; break;
|
|
}
|
|
|
|
//*out << " (" << std::dec << CodeNode->GetUses() << ")";
|
|
|
|
*out << "\n";
|
|
}
|
|
}
|
|
|
|
CurrentIndent = std::max(0, CurrentIndent - 1);
|
|
}
|
|
}
|
|
}
|