mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-07 17:00:19 +02:00
Fixes #1217 Instead of throwing an error and closing down FEX. Instead pass the SIGILL to the guest application. On unhandled instruction implementation the instruction, we instead emit a _Break IR op at that location. A _Break IR op will ensure the context state is synchronized at the point of of the fault and has fairly low overhead. We branch to the dispatcher which does the SRA spilling. Tested this with an application that attempts an AVX512 instruction, catches the fault, and continues onward. With #1383 in place, we also won't pass spurious ERROR_AND_DIE to the guest anymore.
686 lines
21 KiB
C++
686 lines
21 KiB
C++
/*
|
|
$info$
|
|
meta: ir|parser ~ Text -> IR
|
|
tags: ir|parser
|
|
$end_info$
|
|
*/
|
|
|
|
#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 ltrim(std::string String) {
|
|
size_t pos = std::string::npos;
|
|
if ((pos = String.find_first_not_of(" \t\n\r")) != std::string::npos) {
|
|
String.erase(0, pos);
|
|
}
|
|
|
|
return String;
|
|
}
|
|
|
|
std::string rtrim(std::string String) {
|
|
size_t pos = std::string::npos;
|
|
if ((pos = String.find_last_not_of(" \t\n\r")) != std::string::npos) {
|
|
String.erase(String.begin() + pos + 1, String.end());
|
|
}
|
|
|
|
return String;
|
|
}
|
|
|
|
std::string trim(std::string String) {
|
|
return rtrim(ltrim(String));
|
|
}
|
|
|
|
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::BreakReason> DecodeValue(const std::string &Arg) {
|
|
static constexpr std::array<std::string_view, 6> Names = {
|
|
"Unimplemented",
|
|
"Interrupt",
|
|
"Interrupt3",
|
|
"Halt",
|
|
"Overfloat",
|
|
"InvalidInstruction",
|
|
};
|
|
|
|
for (size_t i = 0; i < Names.size(); ++i) {
|
|
if (Names[i] == Arg) {
|
|
return {DecodeFailure::DECODE_OKAY, BreakReason{static_cast<uint8_t>(i)}};
|
|
}
|
|
}
|
|
return {DecodeFailure::DECODE_INVALID_BREAKTYPE, {}};
|
|
}
|
|
|
|
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 = 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(std::istream *text) {
|
|
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;
|
|
|
|
#define IROP_PARSER_ALLOCATE_HELPERS
|
|
#include <FEXCore/IR/IRDefines.inc>
|
|
|
|
|
|
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;
|
|
};
|
|
|
|
// 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 = 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 = 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 = trim(Type);
|
|
|
|
auto DefinitionSize = DecodeValue<FEXCore::IR::TypeDefinition>(Type);
|
|
if (!CheckPrintError(Def, DefinitionSize.first)) {
|
|
return false;
|
|
}
|
|
Def.Size = DefinitionSize.second;
|
|
}
|
|
|
|
Def.Definition = 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 = trim(Type);
|
|
Def.Definition = 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 = 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 = 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 = 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 = 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(std::istream *in) {
|
|
auto parser = std::make_unique<IRParser>(in);
|
|
|
|
if (parser->Loaded) {
|
|
return parser;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
}
|