diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
index 83e4a1bd..e09afbfe 100644
--- a/.github/workflows/format.yml
+++ b/.github/workflows/format.yml
@@ -10,7 +10,7 @@ permissions:
jobs:
format:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
@@ -20,14 +20,14 @@ jobs:
with:
python-version: '3.10'
- - name: Install dependencies
- run: pip install -r assembler_tools/hec-assembler-tools/requirements.txt
+ - name: Install Development Dependencies
+ run: pip install -r requirements-dev.txt
- - name: Install pre-commit
- run: pip install pre-commit
+ - name: Installing Component-specific Dependencies
+ run: pip install -r assembler_tools/hec-assembler-tools/requirements.txt
- - name: Install pylint
- run: pip install pylint
+ - name: Install Apt Dependencies
+ run: sudo apt install -y clang-format-14
- name: Fetch main branch for diff
run: git fetch origin main
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d3467f90..2fd0f8d9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -62,6 +62,7 @@ repos:
- -rn # Only display messages
- -sn # Don't display the score
- --source-roots=p-isa_tools/kerngen # Working directory
+ - --source-roots=p-isa_tools/program_mapper # Added directory
- --source-roots=assembler_tools/hec-assembler-tools # Added directory
- id: clang-format-14
name: clang-format-14
@@ -76,3 +77,4 @@ repos:
files: \.(c|cc|cxx|cpp|h|hpp|hxx)$
args:
- --recursive
+ - --filter=-build/c++17
diff --git a/p-isa_tools/CMakeLists.txt b/p-isa_tools/CMakeLists.txt
index 61b6b2b3..80f767e2 100644
--- a/p-isa_tools/CMakeLists.txt
+++ b/p-isa_tools/CMakeLists.txt
@@ -28,7 +28,7 @@ message(ENABLE_DATA_FORMATS="${ENABLE_DATA_FORMATS}")
option(ENABLE_FUNCTIONAL_MODELER "Enable building of functional modeler" ON)
message(ENABLE_FUNCTIONAL_MODELER="${ENABLE_FUNCTIONAL_MODELER}")
-option(ENABLE_PROGRAM_MAPPER "Enable building of program mapper" OFF)
+option(ENABLE_PROGRAM_MAPPER "Enable building of program mapper" ON)
message(ENABLE_PROGRAM_MAPPER="${ENABLE_PROGRAM_MAPPER}")
option(ENABLE_P_ISA_UTILITIES "Enable building of p-isa utilities" OFF)
diff --git a/p-isa_tools/program_mapper/CMakeLists.txt b/p-isa_tools/program_mapper/CMakeLists.txt
new file mode 100644
index 00000000..9c8a6e06
--- /dev/null
+++ b/p-isa_tools/program_mapper/CMakeLists.txt
@@ -0,0 +1,23 @@
+##############################
+# HERACLES Program Mapper
+##############################
+
+project(program_mapper LANGUAGES CXX)
+
+set(HERACLES_PGM_SOURCES
+ "p_isa/pisakernel.cpp"
+ "p_isa/pisa_graph_optimizer.cpp"
+ "poly_program/polyprogram.cpp"
+ "trace_parser/program_trace_helper.cpp"
+ "p_isa/pisa_test_generator.cpp"
+ "utility_functions.h"
+ "main.cpp"
+)
+
+add_executable(program_mapper ${HERACLES_PGM_SOURCES} ${IDE_HEADERS})
+if(ENABLE_DATA_FORMATS)
+ target_link_libraries(program_mapper PUBLIC HERACLES_DATA_FORMATS::heracles_data_formats)
+ target_compile_definitions(program_mapper PRIVATE ENABLE_DATA_FORMATS)
+endif()
+target_link_libraries(program_mapper PUBLIC nlohmann_json::nlohmann_json snap OpenMP::OpenMP_CXX common)
+target_include_directories(program_mapper PRIVATE ${INCLUDE_DIRS})
diff --git a/p-isa_tools/program_mapper/README.md b/p-isa_tools/program_mapper/README.md
new file mode 100644
index 00000000..5bb3b89e
--- /dev/null
+++ b/p-isa_tools/program_mapper/README.md
@@ -0,0 +1,130 @@
+# HERACLES Program Mapper
+
+## Table of Contents
+1. [Requirements](#requirements)
+2. [Build Configuration](#build-configuration)
+ 1. [Build Type](#build-type)
+ 1. [Third-Party Components](#third-party-components)
+3. [Building](#building)
+4. [Running the Program Mapper](#running-the-program-mapper)
+5. [Code Formatting](#code-formatting)
+
+## Requirements
+
+Current build system uses `CMake`.
+
+Tested Configuration(s)
+- Ubuntu 22.04 (also tested on WSL2)
+- C++17
+- GCC == 11.3
+- CMake >= 3.22.1
+- SNAP (used to support graph features)
+- JSON for Modern CPP >= 3.11
+
+## Build Configuration
+
+The current build system is minimally configurable but will be improved with
+time. The project directory is laid out as follows
+
+- __program_mapper__ *src directory for the program mapper*
+- __common__ *Common code used by p-isa tools*
+
+**NOTE:** If using an IDE then it is recommended to set the `INC_HEADERS` flag
+to include the header files in the project filesystem. This can be done
+via `-DINC_HEADERS=TRUE`.
+
+### Build Type
+
+If no build type is specified, the build system will build in Debug
+mode. Use `-DCMAKE_BUILD_TYPE` configuration variable to set your preferred
+build type:
+
+- `-DCMAKE_BUILD_TYPE=Debug` : debug mode (default if no build type is specified).
+- `-DCMAKE_BUILD_TYPE=Release` : release mode. Compiler optimizations for release enabled.
+- `-DCMAKE_BUILD_TYPE=RelWithDebInfo` : release mode with debug symbols.
+- `-DCMAKE_BUILD_TYPE=MinSizeRel` : release mode optimized for size.
+
+#### Third-Party Components
+This backend requires the following third party components:
+
+- [SNAP](https://github.com/snap-stanford/snap.git)
+- [JSON for modern c++](https://github.com/nlohmann/json)
+
+These external dependencies are fetched and built at configuration time by
+`cmake`, see below how to build the project.
+
+## Building
+Build from the top level of p-isa-tools with Cmake as follows:
+
+```bash
+cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
+cmake --build build -j
+```
+
+Build type can also be changed to `Debug` depending on current needs (Debug
+should be used if the tool is being used to actively debug failing kernels).
+
+## Running the Program Mapper
+
+Located in `build/bin` is an executable called **program_mapper**. This program
+can be used to generate a graph of combined p-isa instructions with a
+supplementary memory file, and a combined p-isa kernel from a program trace.
+The program accepts a number of commandline options to control its usage.
+
+A typical run is of the form
+```bash
+program_mapper
+```
+
+The standard list of currently supported options are listed below.
+```bash
+Usage:
+ program_mapper program_trace kerngen_loc OPTIONS
+
+POSITIONAL ARGUMENTS: 2
+program_trace
+ Location of a file containing a list in csv format for p_isa instructions
+
+kerngen_loc
+ Location of the kerngen.py file
+
+
+OPTIONS:
+--cache_dir, --cache, -c
+ Sets the name of the kernel cache directory [Default: ./kernel_cache]
+
+--disable_cache, --no_cache, -dc
+ Disables the use of a cache for Ninja kernels
+
+--disable_graphs, --graphs, -g
+ Disables graph building and features
+
+--disable_namespace, --nns, -n
+ Disables applying register name spacing on PISAKernel nodes
+
+--dot_file_name, -df
+ Sets the name of the output dot file
+
+--enable_memory_bank_output, --banks, -b
+ Will output P-ISA programs with registers that include hard coded memory banks when enabled
+
+--export_dot, -ed
+ Export seal trace and p_isa graphs to dot file format
+
+--out_dir, --out, -o
+ Sets the location for all output files [Default: ./]
+
+--remove_cache, --rm_cache, -rc
+ Remove the kernel cache directory at the end of the program
+
+--verbose, -v
+ Enables more verbose execution reporting to std out
+
+-h, /h, \h, --help, /help, \help
+ Shows this help.
+```
+
+## Code Formatting
+The repository includes `pre-commit` and `clang-format` hooks to help ensure
+code consistency. It is recommended to install `pre-commit` and `pre-commit
+hooks` prior to committing to repo.
diff --git a/p-isa_tools/program_mapper/main.cpp b/p-isa_tools/program_mapper/main.cpp
new file mode 100644
index 00000000..0a1cd72f
--- /dev/null
+++ b/p-isa_tools/program_mapper/main.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#include
+#include
+#include
+#include
+#include
+
+#include "p_isa/pisa_test_generator.h"
+#include "program_mapper.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+using DATA_TYPE = uint32_t;
+namespace fs = std::filesystem;
+
+inline pisa::ProgramMapperArguments parseCommandLineArguments(int argc, char **argv)
+{
+ pisa::ProgramMapperArguments args;
+ // clang-format off
+ argmap::ArgMap()
+ .separator(argmap::ArgMap::Separator::WHITESPACE)
+ .required()
+ .positional()
+ .arg("program_trace", args.program_trace_location,
+ "Location of a file containing a list in csv format for p_isa instructions", "")
+ .arg("kerngen_loc", args.kerngen, "Location of the kerngen.py file", "")
+ .optional()
+ .toggle()
+ .arg({"--verbose", "-v"}, args.verbose,"Enables more verbose execution reporting to std out", "")
+ .arg({"--export_dot", "-ed"}, args.export_dot, "Export polynomial program and p_isa program graphs to dot file format", "")
+ .arg({"--remove_cache", "--rm_cache", "-rc"}, args.remove_cache,
+ "Remove the kernel cache directory at the end of the program", "")
+ .arg({"--enable_memory_bank_output", "--banks", "-b"}, args.output_memory_bank,
+ "Will output P-ISA programs with registers that include hard coded memory banks when enabled", "")
+ .arg({"--export_trace", "-pb"}, args.export_program_trace,"Exports trace to opposite of input format, CSV <-> Pb", "")
+ .arg({"--enable_intermediates", "-ei"}, args.enable_intermediates,"Enables intermediates by disabling name spacing and other optimizations on intermediate values", "")
+ .toggle(false)
+ .arg({"--disable_graphs", "--graphs", "-g"}, args.generate_graphs,
+ "Disables graph building and features", "")
+ .arg({"--disable_namespace", "--nns", "-n"}, args.apply_name_spacing,
+ "Disables applying register name spacing on PISAKernel nodes", "")
+ .arg({"--disable_cache", "--no_cache", "-dc"}, args.use_kernel_cache,
+ "Disables the use of a cache for Ninja kernels", "")
+ .named()
+ .arg({"--dot_file_name", "-df"}, args.dot_file_name , "Sets the name of the output dot file", "")
+ .arg({"--cache_dir", "--cache", "-c"}, args.cache_dir, "Sets the name of the kernel cache directory")
+ .arg({"--out_dir", "--out", "-o"}, args.out_dir, "Sets the location for all output files")
+ .arg({"--generated_json", "--generate", "-gen"}, args.generated_name, "Enables generation of JSON data file and specifies name")
+ .arg({"--kernel_library", "--kernlib", "-kl"}, args.kernel_library, "Specifies which kernel library to use.")
+ .parse(argc, argv);
+ // clang-format on
+
+ // Post-processing of positional arguments
+ auto strip_substring = [&path = args.program_trace_location](const std::string &substr) -> fs::path {
+ auto ret = path.stem();
+ auto pos = ret.string().find(substr);
+ if (pos != std::string::npos)
+ {
+ ret = ret.string().erase(pos, substr.size());
+ }
+ return ret;
+ };
+
+ args.outfile_prefix = args.out_dir / (strip_substring("_program_trace").string() + "_pisa");
+ if (args.dot_file_name.empty())
+ {
+ args.dot_file_name = args.out_dir / args.program_trace_location.stem();
+ args.dot_file_name.replace_extension("dot");
+ }
+
+ return args;
+}
+
+int main(int argc, char **argv)
+{
+ try
+ {
+ auto arguments = parseCommandLineArguments(argc, argv);
+
+ std::shared_ptr program_trace;
+ // Parses a polynomial program into a vector of HEOPerations
+
+ if (arguments.program_trace_location.extension() == ".csv")
+ {
+ program_trace = PolynomialProgramHelper::parse(arguments.program_trace_location, POLYNOMIAL_PROGRAM_FORMAT::CSV);
+#ifdef ENABLE_DATA_FORMATS
+ if (arguments.export_program_trace)
+ {
+ PolynomialProgramHelper::writeTraceToProtoBuff(program_trace, arguments.program_trace_location.filename().string() + ".bin");
+ }
+#endif
+ }
+#ifdef ENABLE_DATA_FORMATS
+ else if (arguments.program_trace_location.extension() == ".bin")
+ {
+ program_trace = PolynomialProgramHelper::parse(arguments.program_trace_location, POLYNOMIAL_PROGRAM_FORMAT::PROTOBUFF);
+ if (arguments.export_program_trace)
+ {
+ PolynomialProgramHelper::writeTraceToCSV(program_trace, arguments.program_trace_location.filename().string() + ".csv");
+ }
+ }
+#endif
+ else
+ {
+ throw std::runtime_error("Unsupported data format");
+ }
+
+ if (arguments.verbose)
+ std::cout << "Instruction count: " << program_trace->operations().size() << std::endl;
+
+ pisa::ProgramMapper program_mapper;
+ program_mapper.setArguments(arguments);
+ program_mapper.generatePisaProgramFromHEProgram(program_trace);
+
+ return 0;
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cerr << "Caught std::runtime_error in main: " << err.what() << std::endl;
+ return 1;
+ }
+ catch (...)
+ {
+ std::cerr << "Unknown exception caught in main" << std::endl;
+ return 1;
+ }
+}
diff --git a/p-isa_tools/program_mapper/p_isa/pisa_graph_optimizer.cpp b/p-isa_tools/program_mapper/p_isa/pisa_graph_optimizer.cpp
new file mode 100644
index 00000000..715279cf
--- /dev/null
+++ b/p-isa_tools/program_mapper/p_isa/pisa_graph_optimizer.cpp
@@ -0,0 +1,318 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#include "pisa_graph_optimizer.h"
+
+PISAGraphOptimizer::PISAGraphOptimizer()
+{
+}
+
+std::vector PISAGraphOptimizer::generateInstructionStreamFromGraph(graph::Graph &p_isa_graph, bool fixed_order, std::vector instr_order)
+{
+ if (fixed_order == false)
+ {
+ auto instruction_graph = p_isa_graph.clone();
+
+ auto all_nodes = instruction_graph.getNodes();
+ for (auto node : all_nodes)
+ {
+ if (node.GetDat().type != graph::OPERATION)
+ {
+ instruction_graph.removeNodeMaintainConnections(node);
+ }
+ }
+
+ auto instruction_graph_consumable = instruction_graph.clone();
+
+ std::vector>> input_layers;
+ //Layer peel
+ while (instruction_graph_consumable.getNodeCount() > 0)
+ {
+ auto inputs = instruction_graph_consumable.getInputNodes();
+ //input_layers.push_back(inputs);
+ std::vector> layer;
+ for (auto &node : inputs)
+ {
+
+ layer.push_back(instruction_graph.getNode(node.GetId()));
+ //std::cout << *node.GetDat().instruction << std::endl;
+ instruction_graph_consumable.removeNode(node);
+ //std::cout << *node.GetDat().instruction << std::endl;
+ }
+ input_layers.push_back(layer);
+ }
+
+ if (perform_variable_isolation)
+ {
+ isolateGraphVariables(p_isa_graph, input_layers);
+ }
+
+ std::vector instructions;
+
+ for (auto &layer : input_layers)
+ {
+ for (auto &node : layer)
+ {
+ //std::cout << *node.GetDat().instruction << std::endl;
+ instructions.push_back(node.GetDat().instruction);
+ }
+ }
+ return instructions;
+ }
+ else
+ {
+ return instr_order;
+ }
+}
+
+void PISAGraphOptimizer::isolateGraphVariables(graph::Graph &p_isa_graph, std::vector>> &input_layers)
+{
+ //Generate rename black list
+ for (auto &layer : input_layers)
+ {
+ for (auto &node : layer)
+ {
+ nodeLocklist(node, p_isa_graph);
+ }
+ }
+
+ //Adjust variables and generate unique node names
+ for (auto &layer : input_layers)
+ {
+ for (auto &node : layer)
+ {
+ nodeVariableAdjustment(node, p_isa_graph);
+ }
+ }
+ //Correct registers for accumulate ops
+ // for(auto layer = input_layers.rbegin(); layer != input_layers.rend(); layer++){
+ // //for(auto& layer : input_layers.rbegin()) {
+ // for(auto& node : *layer) {
+ // nodeMACVariableAdjustment(node,p_isa_graph);
+ // }
+ // }
+
+ for (auto &layer : input_layers)
+ {
+ for (auto &node : layer)
+ {
+ nodeInstructionAdjustment(node, p_isa_graph);
+ }
+ }
+
+ return;
+}
+
+void PISAGraphOptimizer::applyDuplicateInputVariableSeparation(std::vector &instr_order)
+{
+ std::vector newOrder;
+ for (auto instr : instr_order)
+ {
+ //Check input for matches
+ bool match = false;
+ std::pair matching_indices;
+
+ if (instr->numInputOperands() == 2)
+ {
+ auto &operand_0 = instr->getInputOperand(0);
+ auto &operand_1 = instr->getInputOperand(1);
+ if (operand_0.location() == operand_1.location())
+ {
+ std::cout << "Duplicate input variable detected" << std::endl;
+ match = true;
+ }
+ }
+ else if (instr->numInputOperands() == 3)
+ {
+ auto &operand_0 = instr->getInputOperand(0);
+ auto &operand_1 = instr->getInputOperand(1);
+ auto &operand_2 = instr->getInputOperand(2);
+
+ if (operand_0.location() == operand_1.location())
+ {
+ match = true;
+ matching_indices = std::pair(0, 1);
+ }
+ if (operand_0.location() == operand_2.location())
+ {
+ match = true;
+ matching_indices = std::pair(0, 2);
+ }
+ if (operand_1.location() == operand_2.location())
+ {
+ match = true;
+ matching_indices = std::pair(1, 2);
+ }
+ }
+ if (match == false)
+ {
+ newOrder.push_back(instr);
+ }
+ else
+ {
+ std::cout << "Duplicate input variable detected" << std::endl;
+ auto copy_instr = pisa::instruction::Copy().create();
+ copy_instr->setPMD(instr->PMD());
+ copy_instr->setResidual(instr->residual());
+ copy_instr->addInputOperand(instr->getInputOperand(matching_indices.second));
+ auto output_operand = instr->getInputOperand(matching_indices.second);
+ output_operand.setLocation("copyA" + output_operand.location());
+ copy_instr->addOutputOperand(output_operand);
+ newOrder.push_back(copy_instr);
+ instr->getInputOperand(matching_indices.second).setLocation(output_operand.location());
+ newOrder.push_back(instr);
+ }
+ }
+ instr_order = newOrder;
+ return;
+}
+
+void PISAGraphOptimizer::nodeLocklist(graph::NetworkNode &node, graph::Graph &p_isa_graph)
+{
+ auto p_isa_graph_node = p_isa_graph.getNode(node.GetId());
+ for (int x = 0; x < p_isa_graph_node.GetOutDeg(); x++)
+ {
+ auto target_register = p_isa_graph.getNode(p_isa_graph_node.GetOutNId(x));
+ //If target register is an output, don't touch it, if not, rename it
+ if (target_register.GetOutDeg() == 0 || node.GetDat().instruction->Name() == pisa::instruction::Mac().baseName)
+ {
+ rename_lock_list[target_register.GetDat().label] = true;
+ //std::cout << "Adjusting " << target_register.GetDat().label << " to " << "uid_" + std::to_string(unique_counter++) + "_" << target_register.GetDat().label << std::endl;
+ //target_register.GetDat().label = "uid_" + std::to_string(unique_counter++) + "_" + target_register.GetDat().label;
+ }
+ }
+}
+
+void PISAGraphOptimizer::nodeVariableAdjustment(graph::NetworkNode &node, graph::Graph &p_isa_graph)
+{
+ //node.GetInDeg()
+ //Adjust outputs only
+ auto p_isa_graph_node = p_isa_graph.getNode(node.GetId());
+ for (int x = 0; x < p_isa_graph_node.GetOutDeg(); x++)
+ {
+ auto target_register = p_isa_graph.getNode(p_isa_graph_node.GetOutNId(x));
+ //If target register is an output, don't touch it, if not, rename it
+ if (/*target_register.GetOutDeg() > 0*/ rename_lock_list.count(target_register.GetDat().label) == 0)
+ {
+ std::cout << "Adjusting " << target_register.GetDat().label << " to "
+ << "uid_" + std::to_string(unique_counter++) + "_" << target_register.GetDat().label << std::endl;
+ target_register.GetDat().label = "uid_" + std::to_string(unique_counter++) + "_" + target_register.GetDat().label;
+ }
+ }
+}
+
+void PISAGraphOptimizer::nodeMACVariableAdjustment(graph::NetworkNode &node, graph::Graph &p_isa_graph)
+{
+
+ //
+ auto p_isa_graph_node = p_isa_graph.getNode(node.GetId());
+ //for(int x = 0; x < p_isa_graph_node.GetOutDeg(); x++) {
+ if (p_isa_graph_node.GetDat().instruction->Name() == pisa::instruction::Mac().baseName)
+ {
+ auto target_register_input_reg = p_isa_graph.getNode(p_isa_graph_node.GetInNId(0));
+ auto target_register_output_reg = p_isa_graph.getNode(p_isa_graph_node.GetOutNId(0));
+ //If target register is an output, don't touch it, if not, rename it
+ //if(target_register.GetOutDeg() > 0) {
+ std::cout << "Adjusting Mac Variable registers" << target_register_input_reg.GetDat().label << " to " << target_register_output_reg.GetDat().label << std::endl;
+
+ target_register_input_reg.GetDat().label = target_register_output_reg.GetDat().label;
+ //}
+ }
+ //}
+}
+
+void PISAGraphOptimizer::nodeInstructionAdjustment(graph::NetworkNode &node, graph::Graph &p_isa_graph)
+{
+ //
+ auto p_isa_graph_node = p_isa_graph.getNode(node.GetId());
+ // std::cout << "In degree:"
+ if (p_isa_graph_node.GetDat().instruction->Name() == pisa::instruction::Muli().baseName)
+ {
+ std::cout << "Muli instruction" << std::endl;
+ auto input_node_0 = p_isa_graph.getNode(p_isa_graph_node.GetInNId(0)).GetDat();
+ auto input_node_1 = p_isa_graph.getNode(p_isa_graph_node.GetInNId(1)).GetDat();
+
+ auto &operand_0 = node.GetDat().instruction->getInputOperand(0);
+ auto &operand_1 = node.GetDat().instruction->getInputOperand(1);
+
+ std::cout << "Input label" << 0 << ": " << input_node_0.label << " Immediate: " << operand_0.immediate() << std::endl;
+ std::cout << "Input label" << 1 << ": " << input_node_1.label << " Immediate: " << operand_1.immediate() << std::endl;
+ if (input_node_0.type == graph::IMMEDIATE)
+ {
+ operand_0.setLocation(input_node_1.label);
+ operand_1.setLocation(input_node_0.label);
+ }
+ else
+ {
+ operand_0.setLocation(input_node_0.label);
+ operand_1.setLocation(input_node_1.label);
+ }
+ }
+ else if (p_isa_graph_node.GetDat().instruction->Name() == pisa::instruction::Mac().baseName)
+ {
+ std::cout << "Mac instruction" << std::endl;
+ auto input_node_0 = p_isa_graph.getNode(p_isa_graph_node.GetInNId(0)).GetDat();
+ auto input_node_1 = p_isa_graph.getNode(p_isa_graph_node.GetInNId(1)).GetDat();
+ auto input_node_2 = p_isa_graph.getNode(p_isa_graph_node.GetInNId(2)).GetDat();
+
+ auto output_node_0 = p_isa_graph.getNode(p_isa_graph_node.GetOutNId(0)).GetDat();
+
+ auto &operand_0 = node.GetDat().instruction->getInputOperand(0);
+ auto &operand_1 = node.GetDat().instruction->getInputOperand(1);
+ auto &operand_2 = node.GetDat().instruction->getInputOperand(2);
+
+ auto &output_operand_0 = node.GetDat().instruction->getOutputOperand(0);
+
+ output_operand_0.setLocation(output_node_0.label);
+ if (output_node_0.label == input_node_0.label)
+ {
+ operand_0.setLocation(input_node_0.label);
+ operand_1.setLocation(input_node_1.label);
+ operand_2.setLocation(input_node_2.label);
+ }
+ else if (output_node_0.label == input_node_1.label)
+ {
+ operand_0.setLocation(input_node_1.label);
+ operand_1.setLocation(input_node_0.label);
+ operand_2.setLocation(input_node_2.label);
+ }
+ else if (output_node_0.label == input_node_2.label)
+ {
+ operand_0.setLocation(input_node_2.label);
+ operand_1.setLocation(input_node_0.label);
+ operand_2.setLocation(input_node_1.label);
+ }
+ else
+ {
+ throw std::runtime_error("No match between input and output registers, MAC instruction no valid output!");
+ }
+
+ // std::cout << "Input label" << 0 << ": " << input_node_0.label << " Immediate: " << operand_0.immediate() <getInputOperand(x).immediate() << std::endl;
+ node.GetDat().instruction->getInputOperand(x).setLocation(input_label);
+ }
+ }
+ for (int x = 0; x < p_isa_graph_node.GetOutDeg(); x++)
+ {
+ auto input_label = p_isa_graph.getNode(p_isa_graph_node.GetOutNId(x)).GetDat().label;
+ std::cout << "Output label" << x << ": " << input_label << std::endl;
+ node.GetDat().instruction->getOutputOperand(x).setLocation(input_label);
+ }
+
+ return;
+}
diff --git a/p-isa_tools/program_mapper/p_isa/pisa_graph_optimizer.h b/p-isa_tools/program_mapper/p_isa/pisa_graph_optimizer.h
new file mode 100644
index 00000000..f6c6a111
--- /dev/null
+++ b/p-isa_tools/program_mapper/p_isa/pisa_graph_optimizer.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include
+#include
+#include
+
+class PISAGraphOptimizer
+{
+public:
+ PISAGraphOptimizer();
+ std::vector generateInstructionStreamFromGraph(graph::Graph &p_isa_graph, bool fixed_order, std::vector instr_order);
+
+ void isolateGraphVariables(graph::Graph &p_isa_graph, std::vector>> &layers);
+
+ void applyDuplicateInputVariableSeparation(std::vector &instr_order);
+
+ void nodeLocklist(graph::NetworkNode &node, graph::Graph &p_isa_graph);
+ void nodeVariableAdjustment(graph::NetworkNode &node, graph::Graph &p_isa_graph);
+ void nodeMACVariableAdjustment(graph::NetworkNode &node, graph::Graph &p_isa_graph);
+
+ void nodeInstructionAdjustment(graph::NetworkNode &node, graph::Graph &p_isa_graph);
+ //void setNodeHeights(graph::Graph& p_isa_graph);
+ //int getNodeHeight(graph::NetworkNode& node, graph::Graph p_isa_graph)
+
+ int unique_counter = 1;
+ bool perform_variable_isolation = false;
+ std::map rename_lock_list;
+};
diff --git a/p-isa_tools/program_mapper/p_isa/pisa_test_generator.cpp b/p-isa_tools/program_mapper/p_isa/pisa_test_generator.cpp
new file mode 100644
index 00000000..29b3e54a
--- /dev/null
+++ b/p-isa_tools/program_mapper/p_isa/pisa_test_generator.cpp
@@ -0,0 +1,231 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#include "pisa_test_generator.h"
+#include "functional_modeler/data_handlers/json_data_handler.h"
+#include "functional_modeler/pisa_runtime/pisaprogramruntime.h"
+#include
+#include
+#include
+#include