Files
FEX-Emu--FEX/External/FEXCore/Source/Interface/IR/IRParser.cpp
T
Ryan Houdek ff1d51c7bd FEXCore: Fix-up edge case behaviour on faulting instructions
x86 has six instructions that will fault on us that we mostly handled. A
few of these weren't being handled correctly.

One problem is that RIP needs to synchronize differently depending on
which fault instruction it is. Some instructions fault at the
instruction RIP, some at the instruction afterwards.

Additionally some of the metadata generated around the signal delegation
wasn't correct.

With behaviour of all of these instructions changed, it will now be
easier to switch over to non-faulting guest synchronous signals off of
these instructions. This doesn't go far enough to change that behaviour
yet.

I noticed this when looking at Elden Ring's weird faulting behaviour
with ud2 and `int 0x2d`. This made me investigate since I had a
suspicion that we weren't handling both cases correctly.

With this change, Elden Ring is now stabilized and works under FEX.
2022-07-17 20:56:31 -07:00

689 lines
22 KiB
C++

/*
$info$
meta: ir|parser ~ Text -> IR
tags: ir|parser
$end_info$
*/
#include "Common/StringUtils.h"
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/IR/IREmitter.h>
#include <FEXCore/Utils/LogManager.h>
#include <algorithm>
#include <array>
#include <cstdint>
#include <errno.h>
#include <memory>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <istream>
#include <unordered_map>
namespace FEXCore::IR {
namespace {
enum class DecodeFailure {
DECODE_OKAY,
DECODE_UNKNOWN_TYPE,
DECODE_INVALID,
DECODE_INVALIDCHAR,
DECODE_INVALIDRANGE,
DECODE_INVALIDREGISTERCLASS,
DECODE_UNKNOWN_SSA,
DECODE_INVALID_CONDFLAG,
DECODE_INVALID_MEMOFFSETTYPE,
DECODE_INVALID_FENCETYPE,
DECODE_INVALID_BREAKTYPE,
};
std::string DecodeErrorToString(DecodeFailure Failure) {
switch (Failure) {
case DecodeFailure::DECODE_OKAY: return "Okay";
case DecodeFailure::DECODE_UNKNOWN_TYPE: return "Unknown Type";
case DecodeFailure::DECODE_INVALID: return "Invalid";
case DecodeFailure::DECODE_INVALIDCHAR: return "Invalid starting char";
case DecodeFailure::DECODE_INVALIDRANGE: return "Invalid integer range";
case DecodeFailure::DECODE_INVALIDREGISTERCLASS: return "Invalid register class";
case DecodeFailure::DECODE_UNKNOWN_SSA: return "Unknown SSA value";
case DecodeFailure::DECODE_INVALID_CONDFLAG: return "Invalid Conditional name";
case DecodeFailure::DECODE_INVALID_MEMOFFSETTYPE: return "Invalid Memory Offset Type";
case DecodeFailure::DECODE_INVALID_FENCETYPE: return "Invalid Fence Type";
case DecodeFailure::DECODE_INVALID_BREAKTYPE: return "Invalid Break Reason Type";
}
return "Unknown Error";
}
class IRParser: public FEXCore::IR::IREmitter {
public:
template<typename Type>
std::pair<DecodeFailure, Type> DecodeValue(const std::string &Arg) {
return {DecodeFailure::DECODE_UNKNOWN_TYPE, {}};
}
template<>
std::pair<DecodeFailure, uint8_t> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint8_t Result = strtoul(&Arg.at(1), nullptr, 0);
if (errno == ERANGE) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
return {DecodeFailure::DECODE_OKAY, Result};
}
template<>
std::pair<DecodeFailure, bool> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint8_t Result = strtoul(&Arg.at(1), nullptr, 0);
if (errno == ERANGE || Result > 1) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
return {DecodeFailure::DECODE_OKAY, Result != 0};
}
template<>
std::pair<DecodeFailure, uint16_t> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint16_t Result = strtoul(&Arg.at(1), nullptr, 0);
if (errno == ERANGE) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
return {DecodeFailure::DECODE_OKAY, Result};
}
template<>
std::pair<DecodeFailure, uint32_t> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint32_t Result = strtoul(&Arg.at(1), nullptr, 0);
if (errno == ERANGE) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
return {DecodeFailure::DECODE_OKAY, Result};
}
template<>
std::pair<DecodeFailure, uint64_t> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint64_t Result = strtoull(&Arg.at(1), nullptr, 0);
if (errno == ERANGE) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
return {DecodeFailure::DECODE_OKAY, Result};
}
template<>
std::pair<DecodeFailure, int64_t> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
int64_t Result = (int64_t)strtoull(&Arg.at(1), nullptr, 0);
if (errno == ERANGE) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
return {DecodeFailure::DECODE_OKAY, Result};
}
template<>
std::pair<DecodeFailure, IR::SHA256Sum> DecodeValue(const std::string &Arg) {
IR::SHA256Sum Result;
if (Arg.at(0) != 's' || Arg.at(1) != 'h' || Arg.at(2) != 'a' || Arg.at(3) != '2' || Arg.at(4) != '5' || Arg.at(5) != '6' || Arg.at(6) != ':')
return {DecodeFailure::DECODE_INVALIDCHAR, Result};
auto GetDigit = [](const std::string &Arg, int pos, uint8_t *val) {
auto chr = Arg.at(pos);
if (chr >= '0' && chr <= '9') {
*val = chr - '0';
return true;
} else if (chr >= 'a' && chr <= 'f') {
*val = 10 + chr - 'a';
return true;
} else {
return false;
}
};
for (size_t i = 0; i < sizeof(Result.data); i++) {
uint8_t high, low;
if (!GetDigit(Arg, 7 + 2 * i + 0, &high) || !GetDigit(Arg, 7 + 2 * i + 1, &low)) {
return {DecodeFailure::DECODE_INVALIDRANGE, Result};
}
Result.data[i] = high * 16 + low;
}
return {DecodeFailure::DECODE_OKAY, Result};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::RegisterClassType> DecodeValue(const std::string &Arg) {
if (Arg == "GPR") {
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::GPRClass};
}
else if (Arg == "FPR") {
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::FPRClass};
}
else if (Arg == "GPRPair") {
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::GPRPairClass};
}
else if (Arg == "Complex") {
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::ComplexClass};
}
return {DecodeFailure::DECODE_INVALIDREGISTERCLASS, FEXCore::IR::InvalidClass};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::TypeDefinition> DecodeValue(const std::string &Arg) {
uint8_t Size{}, Elements{1};
int NumArgs = sscanf(Arg.c_str(), "i%hhdv%hhd", &Size, &Elements);
if (NumArgs != 1 && NumArgs != 2) {
return {DecodeFailure::DECODE_INVALID, {}};
}
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::TypeDefinition::Create(Size / 8, Elements)};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::CondClassType> DecodeValue(const std::string &Arg) {
static constexpr std::array<std::string_view, 22> CondNames = {
"EQ",
"NEQ",
"UGE",
"ULT",
"MI",
"PL",
"VS",
"VC",
"UGT",
"ULE",
"SGE",
"SLT",
"SGT",
"SLE",
"Invalid Cond",
"Invalid Cond",
"FLU",
"FGE",
"FLEU",
"FGT",
"FU",
"FNU"
};
for (size_t i = 0; i < CondNames.size(); ++i) {
if (CondNames[i] == Arg) {
return {DecodeFailure::DECODE_OKAY, CondClassType{static_cast<uint8_t>(i)}};
}
}
return {DecodeFailure::DECODE_INVALID_CONDFLAG, {}};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::MemOffsetType> DecodeValue(const std::string &Arg) {
static constexpr std::array<std::string_view, 3> Names = {
"SXTX",
"UXTW",
"SXTW",
};
for (size_t i = 0; i < Names.size(); ++i) {
if (Names[i] == Arg) {
return {DecodeFailure::DECODE_OKAY, MemOffsetType{static_cast<uint8_t>(i)}};
}
}
return {DecodeFailure::DECODE_INVALID_MEMOFFSETTYPE, {}};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::FenceType> DecodeValue(const std::string &Arg) {
static constexpr std::array<std::string_view, 3> Names = {
"Loads",
"Stores",
"LoadStores",
};
for (size_t i = 0; i < Names.size(); ++i) {
if (Names[i] == Arg) {
return {DecodeFailure::DECODE_OKAY, FenceType{static_cast<uint8_t>(i)}};
}
}
return {DecodeFailure::DECODE_INVALID_FENCETYPE, {}};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::BreakDefinition> DecodeValue(const std::string &Arg) {
uint32_t tmp{};
std::stringstream ss{Arg};
BreakDefinition Reason{};
// Seek past '{'
ss.seekg(1, std::ios::cur);
ss >> Reason.ErrorRegister;
// Seek past '.'
ss.seekg(1, std::ios::cur);
ss >> tmp;
Reason.Signal = tmp;
// Seek past '.'
ss.seekg(1, std::ios::cur);
ss >> tmp;
Reason.TrapNumber = tmp;
// Seek past '.'
ss.seekg(1, std::ios::cur);
ss >> tmp;
Reason.si_code = tmp;
if (ss.fail()) {
return {DecodeFailure::DECODE_INVALIDCHAR, {}};
}
else {
return {DecodeFailure::DECODE_OKAY, Reason};
}
}
template<>
std::pair<DecodeFailure, OrderedNode*> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '%') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
// Strip off the type qualifier from the ssa value
std::string SSAName = FEXCore::StringUtils::Trim(Arg);
const size_t ArgEnd = SSAName.find_first_of(' ');
if (ArgEnd != std::string::npos) {
SSAName = SSAName.substr(0, ArgEnd);
}
// Forward declarations may make this not succed
auto Op = SSANameMapper.find(SSAName);
if (Op == SSANameMapper.end()) {
return {DecodeFailure::DECODE_UNKNOWN_SSA, nullptr};
}
return {DecodeFailure::DECODE_OKAY, Op->second};
}
struct LineDefinition {
size_t LineNumber;
bool HasDefinition{};
std::string Definition{};
FEXCore::IR::TypeDefinition Size{};
std::string IROp{};
FEXCore::IR::IROps OpEnum;
bool HasArgs{};
std::vector<std::string> Args;
OrderedNode *Node{};
};
std::vector<std::string> Lines;
std::unordered_map<std::string, OrderedNode*> SSANameMapper;
std::vector<LineDefinition> Defs;
LineDefinition *CurrentDef{};
std::unordered_map<std::string_view, FEXCore::IR::IROps> NameToOpMap;
IRParser(FEXCore::Utils::IntrusivePooledAllocator &ThreadAllocator, std::istream *text)
: IREmitter {ThreadAllocator} {
InitializeNameMap();
std::string TmpLine;
while (!text->eof()) {
std::getline(*text, TmpLine);
if (text->eof()) {
break;
}
if (text->fail()) {
LogMan::Msg::EFmt("Failed to getline on line: {}", Lines.size());
return;
}
Lines.emplace_back(TmpLine);
}
ResetWorkingList();
Loaded = Parse();
}
bool Loaded = false;
bool Parse() {
const auto CheckPrintError = [&](const LineDefinition &Def, DecodeFailure Failure) -> bool {
if (Failure != DecodeFailure::DECODE_OKAY) {
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("Value Couldn't be decoded due to {}", DecodeErrorToString(Failure));
return false;
}
return true;
};
const auto CheckPrintErrorArg = [&](const LineDefinition &Def, DecodeFailure Failure, size_t Arg) -> bool {
if (Failure != DecodeFailure::DECODE_OKAY) {
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("Argument Number {}: {}", Arg + 1, Def.Args[Arg]);
LogMan::Msg::EFmt("Value Couldn't be decoded due to {}", DecodeErrorToString(Failure));
return false;
}
return true;
};
// String parse every line for our definitions
for (size_t i = 0; i < Lines.size(); ++i) {
std::string Line = Lines[i];
LineDefinition Def{};
CurrentDef = &Def;
Def.LineNumber = i;
Line = FEXCore::StringUtils::Trim(Line);
// Skip empty lines
if (Line.empty()) {
continue;
}
if (Line[0] == ';') {
// This is a comment line
// Skip it
continue;
}
size_t CurrentPos{};
// Let's see if this node is assigning something first
if (Line[0] == '%') {
size_t DefinitionEnd = std::string::npos;
if ((DefinitionEnd = Line.find_first_of('=', CurrentPos)) != std::string::npos) {
Def.Definition = Line.substr(0, DefinitionEnd);
Def.Definition = FEXCore::StringUtils::Trim(Def.Definition);
Def.HasDefinition = true;
CurrentPos = DefinitionEnd + 1; // +1 to ensure we go past then assignment
}
else {
LogMan::Msg::EFmt("Error on Line: {}", i);
LogMan::Msg::EFmt("{}", Lines[i]);
LogMan::Msg::EFmt("SSA declaration without assignment");
return false;
}
}
// Check if we are pulling in some IR from the IR Printer
// Prints (%ssa%d) at the start of lines without a definition
if (Line[0] == '(') {
size_t DefinitionEnd = std::string::npos;
if ((DefinitionEnd = Line.find_first_of(')', CurrentPos)) != std::string::npos) {
size_t SSAEnd = std::string::npos;
if ((SSAEnd = Line.find_last_of(' ', DefinitionEnd)) != std::string::npos) {
std::string Type = Line.substr(SSAEnd + 1, DefinitionEnd - SSAEnd - 1);
Type = FEXCore::StringUtils::Trim(Type);
auto DefinitionSize = DecodeValue<FEXCore::IR::TypeDefinition>(Type);
if (!CheckPrintError(Def, DefinitionSize.first)) {
return false;
}
Def.Size = DefinitionSize.second;
}
Def.Definition = FEXCore::StringUtils::Trim(Line.substr(1, std::min(DefinitionEnd, SSAEnd) - 1));
CurrentPos = DefinitionEnd + 1;
}
else {
LogMan::Msg::EFmt("Error on Line: {}", i);
LogMan::Msg::EFmt("{}", Lines[i]);
LogMan::Msg::EFmt("SSA value with numbered SSA provided but no closing parentheses");
return false;
}
}
if (Def.HasDefinition) {
// Let's check if we have a size declared with this variable
size_t NameEnd = std::string::npos;
if ((NameEnd = Def.Definition.find_first_of(' ')) != std::string::npos) {
std::string Type = Def.Definition.substr(NameEnd + 1);
Type = FEXCore::StringUtils::Trim(Type);
Def.Definition = FEXCore::StringUtils::Trim(Def.Definition.substr(0, NameEnd));
auto DefinitionSize = DecodeValue<FEXCore::IR::TypeDefinition>(Type);
if (!CheckPrintError(Def, DefinitionSize.first)) return false;
Def.Size = DefinitionSize.second;
}
if (Def.Definition == "%Invalid") {
LogMan::Msg::EFmt("Error on Line: {}", i);
LogMan::Msg::EFmt("{}", Lines[i]);
LogMan::Msg::EFmt("Definition tried to define reserved %Invalid ssa node");
return false;
}
}
// Let's get the IR op
size_t OpNameEnd = std::string::npos;
std::string RemainingLine = FEXCore::StringUtils::Trim(Line.substr(CurrentPos));
CurrentPos = 0;
if ((OpNameEnd = RemainingLine.find_first_of(" \t\n\r\0", CurrentPos)) != std::string::npos) {
Def.IROp = RemainingLine.substr(CurrentPos, OpNameEnd);
Def.IROp = FEXCore::StringUtils::Trim(Def.IROp);
Def.HasArgs = true;
CurrentPos = OpNameEnd;
}
else {
if (RemainingLine.empty()) {
LogMan::Msg::EFmt("Error on Line: {}", i);
LogMan::Msg::EFmt("{}", Lines[i]);
LogMan::Msg::EFmt("Line without an IROp?");
return false;
}
Def.IROp = RemainingLine;
Def.HasArgs = false;
}
if (Def.HasArgs) {
RemainingLine = FEXCore::StringUtils::Trim(RemainingLine.substr(CurrentPos));
CurrentPos = 0;
if (RemainingLine.empty()) {
// How did we get here?
Def.HasArgs = false;
}
else {
while (!RemainingLine.empty()) {
const size_t ArgEnd = RemainingLine.find(',');
std::string Arg = FEXCore::StringUtils::Trim(RemainingLine.substr(0, ArgEnd));
Def.Args.emplace_back(std::move(Arg));
RemainingLine.erase(0, ArgEnd+1); // +1 to ensure we go past the ','
if (ArgEnd == std::string::npos)
break;
}
}
}
CurrentDef = &Defs.emplace_back(std::move(Def));
}
// Ensure all of the ops are real ops
for(size_t i = 0; i < Defs.size(); ++i) {
auto &Def = Defs[i];
auto Op = NameToOpMap.find(Def.IROp);
if (Op == NameToOpMap.end()) {
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("IROp '{}' doesn't exist", Def.IROp);
return false;
}
Def.OpEnum = Op->second;
}
// Emit the header op
IRPair<IROp_IRHeader> IRHeader;
{
auto &Def = Defs[0];
CurrentDef = &Def;
if (Def.OpEnum != FEXCore::IR::IROps::OP_IRHEADER) {
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("First op needs to be IRHeader. Was '{}'", Def.IROp);
return false;
}
auto CodeBlockCount = DecodeValue<uint64_t>(Def.Args[1]);
if (!CheckPrintError(Def, CodeBlockCount.first)) return false;
IRHeader = _IRHeader(InvalidNode, CodeBlockCount.second);
}
SetWriteCursor(nullptr); // isolate the header from everything following
// Initialize SSANameMapper with Invalid value
SSANameMapper.insert_or_assign("%Invalid", Invalid());
// Spin through the blocks and generate basic block ops
for(size_t i = 0; i < Defs.size(); ++i) {
auto &Def = Defs[i];
if (Def.OpEnum == FEXCore::IR::IROps::OP_CODEBLOCK) {
auto CodeBlock = _CodeBlock(InvalidNode, InvalidNode);
SSANameMapper.insert_or_assign(Def.Definition, CodeBlock.Node);
Def.Node = CodeBlock.Node;
if (i == 1) {
// First code block is the entry block
// Link the header to the first block
IRHeader.first->Blocks = CodeBlock.Node->Wrapped(DualListData.ListBegin());
}
CodeBlocks.emplace_back(CodeBlock.Node);
}
}
SetWriteCursor(nullptr); // isolate the block headers too
// Spin through all the definitions and add the ops to the basic blocks
OrderedNode *CurrentBlock{};
FEXCore::IR::IROp_CodeBlock *CurrentBlockOp{};
for(size_t i = 1; i < Defs.size(); ++i) {
auto &Def = Defs[i];
CurrentDef = &Def;
switch (Def.OpEnum) {
// Special handled
case FEXCore::IR::IROps::OP_IRHEADER:
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("IRHEADER used in the middle of the block!");
return false; // only one OP_IRHEADER allowed per block
case FEXCore::IR::IROps::OP_CODEBLOCK: {
SetWriteCursor(nullptr); // isolate from previous block
if (CurrentBlock != nullptr) {
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("CodeBlock being used inside of already existing codeblock!");
return false;
}
CurrentBlock = Def.Node;
CurrentBlockOp = CurrentBlock->Op(DualListData.DataBegin())->CW<FEXCore::IR::IROp_CodeBlock>();
break;
}
case FEXCore::IR::IROps::OP_BEGINBLOCK: {
if (CurrentBlock == nullptr) {
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("EndBlock being used outside of a block!");
return false;
}
auto Adjust = DecodeValue<OrderedNode*>(Def.Args[0]);
if (!CheckPrintError(Def, Adjust.first)) {
return false;
}
Def.Node = _BeginBlock(Adjust.second);
CurrentBlockOp->Begin = Def.Node->Wrapped(DualListData.ListBegin());
break;
}
case FEXCore::IR::IROps::OP_ENDBLOCK: {
if (CurrentBlock == nullptr) {
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("EndBlock being used outside of a block!");
return false;
}
auto Adjust = DecodeValue<OrderedNode*>(Def.Args[0]);
if (!CheckPrintError(Def, Adjust.first)) {
return false;
}
Def.Node = _EndBlock(Adjust.second);
CurrentBlockOp->Last = Def.Node->Wrapped(DualListData.ListBegin());
CurrentBlock = nullptr;
CurrentBlockOp = nullptr;
break;
}
case FEXCore::IR::IROps::OP_DUMMY: {
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("Dummy op must not be used");
break;
}
#define IROP_PARSER_SWITCH_HELPERS
#include <FEXCore/IR/IRDefines.inc>
default: {
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("Unhandled Op enum '{}' in parser", Def.IROp);
return false;
}
}
if (Def.HasDefinition) {
auto IROp = Def.Node->Op(DualListData.DataBegin());
if (Def.Size.Elements()) {
IROp->Size = Def.Size.Bytes() * Def.Size.Elements();
IROp->ElementSize = Def.Size.Bytes();
}
else {
IROp->Size = Def.Size.Bytes();
IROp->ElementSize = 0;
}
SSANameMapper.insert_or_assign(Def.Definition, Def.Node);
}
}
return true;
}
void InitializeNameMap() {
if (NameToOpMap.empty()) {
for (FEXCore::IR::IROps Op = FEXCore::IR::IROps::OP_DUMMY;
Op <= FEXCore::IR::IROps::OP_LAST;
Op = static_cast<FEXCore::IR::IROps>(static_cast<uint32_t>(Op) + 1)) {
NameToOpMap.insert_or_assign(FEXCore::IR::GetName(Op), Op);
}
}
}
};
} // anon namespace
std::unique_ptr<IREmitter> Parse(FEXCore::Utils::IntrusivePooledAllocator &ThreadAllocator, std::istream *in) {
auto parser = std::make_unique<IRParser>(ThreadAllocator, in);
if (parser->Loaded) {
return parser;
} else {
return nullptr;
}
}
}