Merge pull request #232 from Sonicadvance1/32bit_prefix

More 32bit work
This commit is contained in:
Stefanos Kornilios Mitsis Poiitidis authored and GitHub committed 2020-06-12 23:41:02 +03:00
commit 6fbf0d8fc7
13 files changed
+167 -77

No files matched your search

+43 -43
View File
@@ -641,6 +641,30 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
uint8_t EVEXOp = ReadByte();
return NormalOp(&EVEXTableOps[EVEXOp], EVEXOp);
}
else if (Info->Type == FEXCore::X86Tables::TYPE_REX_PREFIX) {
LogMan::Throw::A(CTX->Config.Is64BitMode, "Got REX prefix in 32bit mode");
DecodeInst->Flags |= DecodeFlags::FLAG_REX_PREFIX;
// Widening displacement
if (Op & 0b1000) {
DecodeInst->Flags |= DecodeFlags::FLAG_REX_WIDENING;
DecodeInst->Flags = (DecodeInst->Flags & ~DecodeFlags::FLAG_OPADDR_MASK) | DecodeFlags::FLAG_WIDENING_SIZE_LAST;
}
// XGPR_B bit set
if (Op & 0b0001)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_B;
// XGPR_X bit set
if (Op & 0b0010)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_X;
// XGPR_R bit set
if (Op & 0b0100)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_R;
return false;
}
return NormalOp(Info, Op);
}
@@ -649,13 +673,12 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
InstructionSize = 0;
Instruction.fill(0);
bool InstructionDecoded = false;
bool ErrorDuringDecoding = false;
DecodeInst = &DecodedBuffer[DecodedSize];
memset(DecodeInst, 0, sizeof(DecodedInst));
DecodeInst->PC = PC;
while (!InstructionDecoded && !ErrorDuringDecoding) {
while (!InstructionDecoded) {
uint8_t Op = ReadByte();
switch (Op) {
case 0x0F: {// Escape Op
@@ -777,49 +800,29 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
DecodeInst->Flags |= DecodeFlags::FLAG_ADDRESS_SIZE;
break;
case 0x26: // ES legacy prefix
if (!CTX->Config.Is64BitMode) {
DecodeInst->Flags |= DecodeFlags::FLAG_ES_PREFIX;
}
break;
case 0x2E: // CS legacy prefix
if (!CTX->Config.Is64BitMode) {
DecodeInst->Flags |= DecodeFlags::FLAG_CS_PREFIX;
}
break;
case 0x36: // SS legacy prefix
if (!CTX->Config.Is64BitMode) {
DecodeInst->Flags |= DecodeFlags::FLAG_SS_PREFIX;
}
break;
case 0x3E: // DS legacy prefix
// Annoyingly GCC generates NOP ops with these prefixes
// Just ignore them for now
// eg. 66 2e 0f 1f 84 00 00 00 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
break;
case 0x40: // REX - 0x40-0x4F
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x45:
case 0x46:
case 0x47:
case 0x48:
case 0x49:
case 0x4A:
case 0x4B:
case 0x4C:
case 0x4D:
case 0x4E:
case 0x4F: {
DecodeInst->Flags |= DecodeFlags::FLAG_REX_PREFIX;
// Widening displacement
if (Op & 0b1000) {
DecodeInst->Flags |= DecodeFlags::FLAG_REX_WIDENING;
DecodeInst->Flags = (DecodeInst->Flags & ~DecodeFlags::FLAG_OPADDR_MASK) | DecodeFlags::FLAG_WIDENING_SIZE_LAST;
if (!CTX->Config.Is64BitMode) {
DecodeInst->Flags |= DecodeFlags::FLAG_DS_PREFIX;
}
// XGPR_B bit set
if (Op & 0b0001)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_B;
// XGPR_X bit set
if (Op & 0b0010)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_X;
// XGPR_R bit set
if (Op & 0b0100)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_R;
break;
break;
}
case 0xF0: // LOCK prefix
DecodeInst->Flags |= DecodeFlags::FLAG_LOCK;
break;
@@ -841,16 +844,13 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
if (NormalOpHeader(&FEXCore::X86Tables::BaseOps[Op], Op)) {
InstructionDecoded = true;
}
else {
LogMan::Msg::E("Error during instruction decoding");
ErrorDuringDecoding = true;
}
break;
}
}
}
return !ErrorDuringDecoding;
return true;
}
void Decoder::BranchTargetInMultiblockRange() {
@@ -6824,6 +6824,11 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{0xFC, 2, &OpDispatchBuilder::FLAGControlOp},
};
const std::vector<std::tuple<uint8_t, uint8_t, X86Tables::OpDispatchPtr>> BaseOpTable_32 = {
{0x40, 8, &OpDispatchBuilder::INCOp},
{0x48, 8, &OpDispatchBuilder::DECOp},
};
const std::vector<std::tuple<uint8_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr>> TwoByteOpTable = {
// Instructions
{0x00, 1, nullptr}, // GROUP 6
@@ -7750,6 +7755,9 @@ constexpr uint16_t PF_F2 = 3;
};
InstallToTable(FEXCore::X86Tables::BaseOps, BaseOpTable);
if (Mode == Context::MODE_32BIT) {
InstallToTable(FEXCore::X86Tables::BaseOps, BaseOpTable_32);
}
InstallToTable(FEXCore::X86Tables::SecondBaseOps, TwoByteOpTable);
InstallToTable(FEXCore::X86Tables::PrimaryInstGroupOps, PrimaryGroupOpTable);
@@ -9,22 +9,19 @@ void InitializeBaseTables(Context::OperatingMode Mode) {
const U8U8InfoStruct BaseOpTable[] = {
// Prefixes
// Operand size overide
{0x66, 1, X86InstInfo{"", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
{0x66, 1, X86InstInfo{"", TYPE_PREFIX, FLAGS_NONE, 0, nullptr}},
// Address size override
{0x67, 1, X86InstInfo{"", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
{0x2E, 1, X86InstInfo{"CS", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
{0x3E, 1, X86InstInfo{"DS", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
{0x67, 1, X86InstInfo{"", TYPE_PREFIX, FLAGS_NONE, 0, nullptr}},
{0x26, 1, X86InstInfo{"ES", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
{0x2E, 1, X86InstInfo{"CS", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
{0x36, 1, X86InstInfo{"SS", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
{0x3E, 1, X86InstInfo{"DS", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
// These are still invalid on 64bit
{0x64, 1, X86InstInfo{"FS", TYPE_PREFIX, FLAGS_NONE, 0, nullptr}},
{0x65, 1, X86InstInfo{"GS", TYPE_PREFIX, FLAGS_NONE, 0, nullptr}},
{0x36, 1, X86InstInfo{"SS", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
{0xF0, 1, X86InstInfo{"LOCK", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
{0xF2, 1, X86InstInfo{"REPNE", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
{0xF3, 1, X86InstInfo{"REP", TYPE_LEGACY_PREFIX, FLAGS_NONE, 0, nullptr}},
// REX
{0x40, 16, X86InstInfo{"", TYPE_REX_PREFIX, FLAGS_NONE, 0, nullptr}},
{0xF0, 1, X86InstInfo{"LOCK", TYPE_PREFIX, FLAGS_NONE, 0, nullptr}},
{0xF2, 1, X86InstInfo{"REPNE", TYPE_PREFIX, FLAGS_NONE, 0, nullptr}},
{0xF3, 1, X86InstInfo{"REP", TYPE_PREFIX, FLAGS_NONE, 0, nullptr}},
// Instructions
{0x00, 1, X86InstInfo{"ADD", TYPE_INST, GenFlagsSameSize(SIZE_8BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
@@ -135,9 +132,9 @@ void InitializeBaseTables(Context::OperatingMode Mode) {
{0x89, 1, X86InstInfo{"MOV", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
{0x8A, 1, X86InstInfo{"MOV", TYPE_INST, GenFlagsSameSize(SIZE_8BIT) | FLAGS_MODRM, 0, nullptr}},
{0x8B, 1, X86InstInfo{"MOV", TYPE_INST, FLAGS_MODRM, 0, nullptr}},
{0x8C, 1, X86InstInfo{"MOV", TYPE_INVALID, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
{0x8C, 1, X86InstInfo{"MOV", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
{0x8D, 1, X86InstInfo{"LEA", TYPE_INST, GenFlagsSameSize(SIZE_64BITDEF) | FLAGS_MODRM, 0, nullptr}},
{0x8E, 1, X86InstInfo{"MOV", TYPE_INVALID, FLAGS_MODRM, 0, nullptr}}, // MOV seg, modrM == invalid on x86-64
{0x8E, 1, X86InstInfo{"MOV", TYPE_INST, FLAGS_MODRM, 0, nullptr}}, // MOV seg, modrM == invalid on x86-64
{0x8F, 1, X86InstInfo{"POP", TYPE_INST, GenFlagsSameSize(SIZE_64BITDEF) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_DEBUG_MEM_ACCESS, 0, nullptr}},
{0x90, 8, X86InstInfo{"XCHG", TYPE_INST, FLAGS_SF_REX_IN_BYTE | FLAGS_SF_SRC_RAX, 0, nullptr}},
{0x98, 1, X86InstInfo{"CDQE", TYPE_INST, FLAGS_SF_DST_RAX | FLAGS_SF_SRC_RAX, 0, nullptr}},
@@ -246,7 +243,24 @@ void InitializeBaseTables(Context::OperatingMode Mode) {
{0xC4, 2, X86InstInfo{"", TYPE_VEX_TABLE_PREFIX, FLAGS_NONE, 0, nullptr}},
};
const U8U8InfoStruct BaseOpTable_64[] = {
// REX
{0x40, 16, X86InstInfo{"", TYPE_REX_PREFIX, FLAGS_NONE, 0, nullptr}},
};
const U8U8InfoStruct BaseOpTable_32[] = {
{0x40, 8, X86InstInfo{"INC", TYPE_INST, FLAGS_SF_REX_IN_BYTE, 0, nullptr}},
{0x48, 8, X86InstInfo{"DEC", TYPE_INST, FLAGS_SF_REX_IN_BYTE, 0, nullptr}},
};
GenerateTable(BaseOps, BaseOpTable, sizeof(BaseOpTable) / sizeof(BaseOpTable[0]));
if (Mode == Context::MODE_64BIT) {
GenerateTable(BaseOps, BaseOpTable_64, sizeof(BaseOpTable_64) / sizeof(BaseOpTable_64[0]));
}
else {
GenerateTable(BaseOps, BaseOpTable_32, sizeof(BaseOpTable_32) / sizeof(BaseOpTable_32[0]));
}
}
}
+9 -5
View File
@@ -28,12 +28,16 @@ constexpr uint32_t FLAG_REX_WIDENING = (1 << 7);
constexpr uint32_t FLAG_REX_XGPR_B = (1 << 8);
constexpr uint32_t FLAG_REX_XGPR_X = (1 << 9);
constexpr uint32_t FLAG_REX_XGPR_R = (1 << 10);
constexpr uint32_t FLAG_FS_PREFIX = (1 << 11);
constexpr uint32_t FLAG_GS_PREFIX = (1 << 12);
constexpr uint32_t FLAG_REP_PREFIX = (1 << 13);
constexpr uint32_t FLAG_REPNE_PREFIX = (1 << 14);
constexpr uint32_t FLAG_ES_PREFIX = (1 << 11);
constexpr uint32_t FLAG_CS_PREFIX = (1 << 12);
constexpr uint32_t FLAG_SS_PREFIX = (1 << 13);
constexpr uint32_t FLAG_DS_PREFIX = (1 << 14);
constexpr uint32_t FLAG_FS_PREFIX = (1 << 15);
constexpr uint32_t FLAG_GS_PREFIX = (1 << 16);
constexpr uint32_t FLAG_REP_PREFIX = (1 << 17);
constexpr uint32_t FLAG_REPNE_PREFIX = (1 << 18);
// Size flags
constexpr uint32_t FLAG_SIZE_DST_OFF = 15;
constexpr uint32_t FLAG_SIZE_DST_OFF = 19;
constexpr uint32_t FLAG_SIZE_SRC_OFF = FLAG_SIZE_DST_OFF + 3;
constexpr uint32_t SIZE_MASK = 0b111;
constexpr uint32_t SIZE_DEF = 0b000; // This should be invalid past decoding
+18
View File
@@ -58,6 +58,10 @@ class ABI(Flag) :
ABI_WIN64 = 1
ABI_NONE = 2
class Mode(Flag) :
MODE_32 = 0
MODE_64 = 1
RegStringLookup = {
"NONE": Regs.REG_NONE,
"RAX": Regs.REG_RAX,
@@ -114,11 +118,17 @@ ABIStringLookup = {
"NONE": ABI.ABI_NONE,
}
ModeStringLookup = {
"32BIT": Mode.MODE_32,
"64BIT": Mode.MODE_64,
}
def parse_json(json_text, output_file):
# Default options
OptionMatch = Regs.REG_INVALID
OptionIgnore = Regs.REG_NONE
OptionABI = ABI.ABI_SYSTEMV
OptionMode = Mode.MODE_64
OptionStackSize = 4096
OptionEntryPoint = 1
OptionRegData = {}
@@ -164,6 +174,13 @@ def parse_json(json_text, output_file):
sys.exit("Invalid ABI")
OptionABI = ABIStringLookup[data]
if ("MODE" in json_object):
data = json_object["MODE"]
data = data.upper()
if not (data in ModeStringLookup):
sys.exit("Invalid Mode")
OptionMode = ModeStringLookup[data]
if ("STACKSIZE" in json_object):
data = json_object["STACKSIZE"]
OptionStackSize = int(data, 0)
@@ -212,6 +229,7 @@ def parse_json(json_text, output_file):
config_file.write(struct.pack('Q', OptionStackSize))
config_file.write(struct.pack('Q', OptionEntryPoint))
config_file.write(struct.pack('I', OptionABI.value))
config_file.write(struct.pack('I', OptionMode.value))
# Number of memory regions
config_file.write(struct.pack('I', len(OptionMemoryRegions)))
+5
View File
@@ -263,6 +263,8 @@ namespace FEX::HarnessHelper {
return Matches;
}
bool Is64BitMode() const { return BaseConfig.OptionMode == 1; }
private:
FEX::Config::Value<bool> ConfigDumpGPRs{"DumpGPRs", false};
@@ -272,6 +274,7 @@ namespace FEX::HarnessHelper {
uint64_t OptionStackSize;
uint64_t OptionEntryPoint;
uint32_t OptionABI;
uint32_t OptionMode;
uint32_t OptionMemoryRegionCount;
uint32_t OptionRegDataCount;
uint8_t AdditionalData[];
@@ -366,6 +369,8 @@ namespace FEX::HarnessHelper {
return Config.CompareStates(State1, State2);
}
bool Is64BitMode() const { return Config.Is64BitMode(); }
private:
constexpr static uint64_t STACK_SIZE = PAGE_SIZE;
// Zero is special case to know when we are done
+3 -3
View File
@@ -69,21 +69,21 @@ int main(int argc, char **argv, char **const envp) {
auto SHM = FEXCore::SHM::AllocateSHMRegion(1ULL << 34);
auto CTX = FEXCore::Context::CreateNewContext();
FEX::HarnessHelper::HarnessCodeLoader Loader{Args[0], Args[1].c_str()};
FEXCore::Context::SetCustomCPUBackendFactory(CTX, VMFactory::CPUCreationFactory);
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_UNIFIED_MEMORY, 0); // ensure TestHarnessRunner doesn't enable UnifiedMemory.
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_DEFAULTCORE, CoreConfig() > 3 ? FEXCore::Config::CONFIG_CUSTOM : CoreConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_MULTIBLOCK, MultiblockConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_SINGLESTEP, SingleStepConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_MAXBLOCKINST, BlockSizeConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_IS64BIT_MODE, true);
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode());
FEXCore::Context::SetCustomCPUBackendFactory(CTX, VMFactory::CPUCreationFactory);
FEXCore::Context::AddGuestMemoryRegion(CTX, SHM);
FEXCore::Context::InitializeContext(CTX);
FEX::HarnessHelper::HarnessCodeLoader Loader{Args[0], Args[1].c_str()};
bool Result1 = FEXCore::Context::InitCore(CTX, &Loader);
if (!Result1)
+19 -8
View File
@@ -5,27 +5,38 @@ file(GLOB_RECURSE ASM_SOURCES CONFIGURE_DEPENDS *.asm)
set(ASM_DEPENDS "")
foreach(ASM_SRC ${ASM_SOURCES})
file(RELATIVE_PATH REL_ASM ${CMAKE_SOURCE_DIR} ${ASM_SRC})
file(RELATIVE_PATH REL_TEST_ASM ${CMAKE_CURRENT_SOURCE_DIR} ${ASM_SRC})
get_filename_component(ASM_NAME ${ASM_SRC} NAME)
get_filename_component(ASM_DIR "${REL_ASM}" DIRECTORY)
set(OUTPUT_ASM_FOLDER "${CMAKE_BINARY_DIR}/${ASM_DIR}")
# Generate build directory
add_custom_command(OUTPUT ${OUTPUT_ASM_FOLDER}
COMMAND ${CMAKE_COMMAND} -E make_directory "${OUTPUT_ASM_FOLDER}")
# Generate a temporary file
set(ASM_TMP "${ASM_NAME}_TMP.asm")
set(TMP_FILE "${CMAKE_CURRENT_BINARY_DIR}/${ASM_TMP}")
set(ASM_TMP "${ASM_NAME}_TMP.asm")
set(TMP_FILE "${OUTPUT_ASM_FOLDER}/${ASM_TMP}")
add_custom_command(OUTPUT ${TMP_FILE}
DEPENDS "${OUTPUT_ASM_FOLDER}"
DEPENDS "${ASM_SRC}"
COMMAND "cp" ARGS "${ASM_SRC}" "${TMP_FILE}"
COMMAND "sed" ARGS "-i" "-e" "\'1s;^;BITS 64\\n;\'" "-e" "\'\$\$a\\ret\\n\'" "${TMP_FILE}"
)
set(OUTPUT_NAME "${ASM_NAME}.bin")
set(OUTPUT_CONFIG_NAME "${ASM_NAME}.config.bin")
set(OUTPUT_NAME "${OUTPUT_ASM_FOLDER}/${ASM_NAME}.bin")
set(OUTPUT_CONFIG_NAME "${OUTPUT_ASM_FOLDER}/${ASM_NAME}.config.bin")
add_custom_command(OUTPUT ${OUTPUT_NAME}
DEPENDS "${TMP_FILE}"
COMMAND "nasm" ARGS "${TMP_FILE}" "-o" "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_NAME}")
COMMAND "nasm" ARGS "${TMP_FILE}" "-o" "${OUTPUT_NAME}")
add_custom_command(OUTPUT ${OUTPUT_CONFIG_NAME}
DEPENDS "${ASM_SRC}"
COMMAND "python3" ARGS "${CMAKE_SOURCE_DIR}/Scripts/json_asm_config_parse.py" "${ASM_SRC}" "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_CONFIG_NAME}")
DEPENDS "${CMAKE_SOURCE_DIR}/Scripts/json_asm_config_parse.py"
COMMAND "python3" ARGS "${CMAKE_SOURCE_DIR}/Scripts/json_asm_config_parse.py" "${ASM_SRC}" "${OUTPUT_CONFIG_NAME}")
list(APPEND ASM_DEPENDS "${OUTPUT_NAME};${OUTPUT_CONFIG_NAME}")
@@ -50,13 +61,13 @@ foreach(ASM_SRC ${ASM_SOURCES})
list(GET TEST_ARGS ${Index} ARGS)
list(GET TEST_ARGS ${TEST_NAME_INDEX} TEST_DESC)
set(TEST_NAME "${TEST_DESC}/Test_${ASM_NAME}")
set(TEST_NAME "${TEST_DESC}/Test_${REL_TEST_ASM}")
string(REPLACE " " ";" ARGS_LIST ${ARGS})
add_test(NAME ${TEST_NAME}
COMMAND "python3" "${CMAKE_SOURCE_DIR}/Scripts/testharness_runner.py"
"${CMAKE_SOURCE_DIR}/unittests/ASM/Known_Failures"
"${CMAKE_SOURCE_DIR}/unittests/ASM/Disabled_Tests"
"Test_${ASM_NAME}"
"Test_${REL_TEST_ASM}"
"${CMAKE_BINARY_DIR}/Bin/TestHarnessRunner"
${ARGS_LIST} "${OUTPUT_NAME}" "${OUTPUT_CONFIG_NAME}")
# This will cause the ASM tests to fail if it can't find the TestHarness or ASMN files
+1 -1
View File
@@ -1 +1 @@
Test_D9_F2.asm
Test_X87/D9_F2.asm
+4 -4
View File
@@ -1,4 +1,4 @@
Test_14_XX_02.asm
Test_0F_2E.asm
Test_0F_2F.asm
Test_D9_FD.asm
Test_Secondary/14_XX_02.asm
Test_TwoByte/0F_2E.asm
Test_TwoByte/0F_2F.asm
Test_X87/D9_FD.asm
+8
View File
@@ -0,0 +1,8 @@
%ifdef CONFIG
{
"Mode": "32BIT"
}
%endif
BITS 32;
hlt
+21
View File
@@ -0,0 +1,21 @@
%ifdef CONFIG
{
"Mode": "32BIT",
"RegData": {
"RAX": "0xFFFFFFFF"
}
}
%endif
BITS 32;
mov esp, 0xe8000000
mov eax, 0
call .label
hlt
.label:
mov eax, -1
ret
+1
View File
@@ -14,6 +14,7 @@ foreach(IR_SRC ${IR_SOURCES})
add_custom_command(OUTPUT ${OUTPUT_CONFIG_NAME}
DEPENDS "${IR_SRC}"
DEPENDS "${CMAKE_SOURCE_DIR}/Scripts/json_asm_config_parse.py"
COMMAND "python3" ARGS "${CMAKE_SOURCE_DIR}/Scripts/json_ir_config_parse.py" "${IR_SRC}" "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_CONFIG_NAME}")
list(APPEND IR_DEPENDS "${OUTPUT_CONFIG_NAME}")