Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

766 lines
31 KiB
C++

/* ************************************************************************
* Copyright (C) 2025-2026 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* ************************************************************************ */
/**
* @file GenLogicalIR.cpp
* @brief Generates high-level IR instruction class definitions and builder methods
*
* This generator creates:
* 1. IR instruction class definitions (LogicalInstructions_generated.hpp)
* 2. Builder method forward declarations (StinkyBuilder_decls_generated.inc)
* 3. Builder method implementations (StinkyBuilder_impls_generated.inc)
* (Logical IR -> ASM mnemonic is per-arch via LogicalToAsmMappings_generated.inc from
* GenLogicalToAsmMapping.cpp)
*/
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
namespace stinkytofu {
// IR instruction definition (Logical IR has no mnemonics; ASM mnemonic is per-arch via
// LogicalToAsmMappings)
struct IRInstDef {
std::string className; // e.g., "VAddF32"
std::string comment; // e.g., "Vector add F32: dst = src0 + src1"
int numSrcs; // Total number of source operands (required + optional)
int numRequiredSrcs; // Number of required sources (rest are optional pointers)
bool hasDest; // Whether it has a destination operand
std::string category; // e.g., "Vector Arithmetic", "Scalar Bitwise"
bool supportsDPP; // Whether this instruction supports DPP modifiers
bool supportsSDWA; // Whether this instruction supports SDWA modifiers
bool hasDS; // Whether this instruction has DS modifiers (for LDS operations)
std::string flags; // Instruction flags (e.g., "IF_DSRead|IF_Commutative|IF_VALU")
IRInstDef(const std::string& cls, const std::string& cmt, int srcs, bool dest = true,
const std::string& cat = "", bool dpp = false, bool sdwa = false, bool ds = false,
const std::string& flags = "",
int reqSrcs = -1) // -1 means all sources are required (at end for backward compat)
: className(cls),
comment(cmt),
numSrcs(srcs),
numRequiredSrcs(reqSrcs == -1 ? srcs : reqSrcs),
hasDest(dest),
category(cat),
supportsDPP(dpp),
supportsSDWA(sdwa),
hasDS(ds),
flags(flags) {}
};
// Derive parse name (snake_case) from logical class name for getOpcodeMnemonic/parseOpcode.
static std::string classNameToParseName(const std::string& className) {
std::string out;
out.reserve(className.size() + 4);
for (size_t i = 0; i < className.size(); ++i) {
char c = className[i];
if (c >= 'A' && c <= 'Z') {
if (i != 0) out += '_';
out += char(c - 'A' + 'a');
} else
out += c;
}
return out;
}
// Define all high-level IR instructions
// This is the "single source of truth" replacing manual class definitions
// Instruction definitions are in a separate file for maintainability
static std::vector<IRInstDef> getIRInstructions() {
return {
#include "../../src/ir/logical/LogicalInstructionDefs.inc"
};
}
// Generate special instruction factory functions (pure enum design with specialData_)
static bool genSpecialMFMAClasses(std::ofstream& out) {
// Factory functions for special instructions (MFMA, Label, etc.)
out << " // "
"========================================================================\n";
out << " // Special Instruction Factory Functions (Pure Enum Design)\n";
out << " // "
"========================================================================\n\n";
out << " /**\n";
out << " * @brief MFMA (Matrix Fused Multiply-Add) factory function\n";
out << " * \n";
out << " * Registers stored in: dests[0]=acc, srcs[0]=a, srcs[1]=b, srcs[2]=acc2 "
"(optional)\n";
out << " * Metadata stored in: specialData_ as MFMAData*\n";
out << " */\n";
out << " inline LogicalInstruction* MFMA(\n";
out << " const std::string& instType,\n";
out << " const std::string& accType,\n";
out << " int m,\n";
out << " int n,\n";
out << " int k,\n";
out << " int blocks,\n";
out << " bool mfma1k,\n";
out << " const StinkyRegister& acc,\n";
out << " const StinkyRegister& a,\n";
out << " const StinkyRegister& b,\n";
out << " const StinkyRegister* acc2 = nullptr,\n";
out << " bool neg = false,\n";
out << " const std::string& matrixAFmt = \"\",\n";
out << " const std::string& matrixBFmt = \"\",\n";
out << " bool scaled = false,\n";
out << " bool scaleOperands = false,\n";
out << " const std::string& comment = \"\")\n";
out << " {\n";
out << " auto* inst = IRBase::createIR<LogicalInstruction>(logical::MFMA);\n";
out << " \n";
out << " // Populate registers\n";
out << " inst->dests.push_back(acc);\n";
out << " inst->srcs.push_back(a);\n";
out << " inst->srcs.push_back(b);\n";
out << " if (acc2) inst->srcs.push_back(*acc2);\n";
out << " \n";
out << " // Create and set special data\n";
out << " auto* data = new MFMAData(instType, accType, m, n, k, blocks, mfma1k, "
"neg, matrixAFmt, matrixBFmt, scaled, scaleOperands);\n";
out << " inst->setSpecialData(data);\n";
out << " inst->comment = comment;\n";
out << " \n";
out << " return inst;\n";
out << " }\n\n";
// MXMFMA factory
out << " /**\n";
out << " * @brief MXMFMA (MX format MFMA with scale factors) factory function\n";
out << " * \n";
out << " * Registers: dests[0]=acc, srcs[0]=a, srcs[1]=b, srcs[2]=acc2, srcs[3]=mxsa, "
"srcs[4]=mxsb\n";
out << " * Metadata stored in: specialData_ as MXMFMAData*\n";
out << " */\n";
out << " inline LogicalInstruction* MXMFMA(\n";
out << " const std::string& instType,\n";
out << " const std::string& accType,\n";
out << " const std::string& mxScaleATypeStr,\n";
out << " const std::string& mxScaleBTypeStr,\n";
out << " int m,\n";
out << " int n,\n";
out << " int k,\n";
out << " int block,\n";
out << " const StinkyRegister& acc,\n";
out << " const StinkyRegister& a,\n";
out << " const StinkyRegister& b,\n";
out << " const StinkyRegister& acc2,\n";
out << " const StinkyRegister& mxsa,\n";
out << " const StinkyRegister& mxsb,\n";
out << " bool reuseA = false,\n";
out << " bool reuseB = false,\n";
out << " const std::string& matrixAFmt = \"\",\n";
out << " const std::string& matrixBFmt = \"\",\n";
out << " const std::string& comment = \"\")\n";
out << " {\n";
out << " auto* inst = IRBase::createIR<LogicalInstruction>(logical::MXMFMA);\n";
out << " \n";
out << " // Populate registers\n";
out << " inst->dests.push_back(acc);\n";
out << " inst->srcs.push_back(a);\n";
out << " inst->srcs.push_back(b);\n";
out << " inst->srcs.push_back(acc2);\n";
out << " inst->srcs.push_back(mxsa);\n";
out << " inst->srcs.push_back(mxsb);\n";
out << " \n";
out << " // Create and set special data\n";
out << " auto* data = new MXMFMAData(instType, accType, mxScaleATypeStr, "
"mxScaleBTypeStr,\n";
out << " m, n, k, block, reuseA, reuseB, matrixAFmt, "
"matrixBFmt);\n";
out << " inst->setSpecialData(data);\n";
out << " inst->comment = comment;\n";
out << " \n";
out << " return inst;\n";
out << " }\n\n";
// SMFMA factory
out << " /**\n";
out << " * @brief SMFMA (Sparse MFMA) factory function\n";
out << " * \n";
out << " * Registers: dests[0]=acc, srcs[0]=a, srcs[1]=b, srcs[2]=metadata\n";
out << " * Metadata stored in: specialData_ as SMFMAData*\n";
out << " */\n";
out << " inline LogicalInstruction* SMFMA(\n";
out << " const std::string& instType,\n";
out << " const std::string& accType,\n";
out << " int m,\n";
out << " int n,\n";
out << " int k,\n";
out << " int blocks,\n";
out << " bool mfma1k,\n";
out << " const StinkyRegister& acc,\n";
out << " const StinkyRegister& a,\n";
out << " const StinkyRegister& b,\n";
out << " const StinkyRegister& metadata,\n";
out << " bool neg = false,\n";
out << " const std::string& comment = \"\")\n";
out << " {\n";
out << " auto* inst = IRBase::createIR<LogicalInstruction>(logical::SMFMA);\n";
out << " \n";
out << " // Populate registers\n";
out << " inst->dests.push_back(acc);\n";
out << " inst->srcs.push_back(a);\n";
out << " inst->srcs.push_back(b);\n";
out << " inst->srcs.push_back(metadata);\n";
out << " \n";
out << " // Create and set special data\n";
out << " auto* data = new SMFMAData(instType, accType, m, n, k, blocks, mfma1k, "
"neg);\n";
out << " inst->setSpecialData(data);\n";
out << " inst->comment = comment;\n";
out << " \n";
out << " return inst;\n";
out << " }\n\n";
// Label factory
out << " // "
"========================================================================\n";
out << " // Control Flow Instructions\n";
out << " // "
"========================================================================\n\n";
out << " /**\n";
out << " * @brief Label factory function for control flow\n";
out << " * \n";
out << " * Defines a label that can be used as a branch target.\n";
out << " * Labels have no operands and do not produce output.\n";
out << " * Metadata stored in: specialData_ as LogicalLabelData*\n";
out << " */\n";
out << " inline LogicalInstruction* Label(const std::string& labelName)\n";
out << " {\n";
out << " auto* inst = IRBase::createIR<LogicalInstruction>(logical::Label);\n";
out << " \n";
out << " // Labels have no operands\n";
out << " \n";
out << " // Create and set special data\n";
out << " auto* data = new LogicalLabelData(labelName);\n";
out << " inst->setSpecialData(data);\n";
out << " \n";
out << " return inst;\n";
out << " }\n\n";
return true;
}
// Generate IR instruction class definitions
// Generate opcode enum values
static bool genOpcodeEnum(const std::string& outdir) {
std::ofstream out(outdir + "/LogicalOpcodes_generated.inc");
if (!out) {
std::cerr << "Failed to open LogicalOpcodes_generated.inc for writing\n";
return false;
}
out << "// Auto-generated by TableGen - DO NOT EDIT\n";
out << "// High-level IR opcode enumeration\n\n";
int count = 0;
// Generate opcode for each instruction
for (const auto& inst : getIRInstructions()) {
out << " " << inst.className << " = " << (count + 1) << ",\n";
count++;
}
std::cout << "Generated " << count << " opcode enum values -> LogicalOpcodes_generated.inc\n";
return true;
}
// Generate opcode to string mapping tables
static bool genOpcodeMappings(const std::string& outdir) {
std::ofstream out(outdir + "/stinkytofu/ir/logical/LogicalOpcode.cpp");
if (!out) {
std::cerr << "Failed to open LogicalOpcode.cpp for writing\n";
return false;
}
// File header
out << "/* ************************************************************************\n";
out << " * Copyright (C) 2025-2026 Advanced Micro Devices, Inc.\n";
out << " * AUTO-GENERATED FILE - DO NOT EDIT\n";
out << " * Generated by: tools/tablegen/GenLogicalIR.cpp\n";
out << " * ************************************************************************ */\n\n";
out << "#include \"stinkytofu/ir/logical/LogicalOpcode.hpp\"\n\n";
out << "namespace stinkytofu\n";
out << "{\n";
out << "namespace logical\n";
out << "{\n\n";
// Generate getOpcodeName function
out << "const char* getOpcodeName(Opcode opcode)\n";
out << "{\n";
out << " switch (opcode)\n";
out << " {\n";
out << " case UNKNOWN:\n";
out << " return \"UNKNOWN\";\n";
for (const auto& inst : getIRInstructions()) {
out << " case " << inst.className << ":\n";
out << " return \"" << inst.className << "\";\n";
}
// Special opcodes (not auto-generated)
out << " case MFMA:\n";
out << " return \"MFMA\";\n";
out << " case MXMFMA:\n";
out << " return \"MXMFMA\";\n";
out << " case SMFMA:\n";
out << " return \"SMFMA\";\n";
out << " case Label:\n";
out << " return \"Label\";\n";
out << " case IntrinsicCall:\n";
out << " return \"IntrinsicCall\";\n";
out << " case SWaitAlu:\n";
out << " return \"SWaitAlu\";\n";
out << " case SchedulingFence:\n";
out << " return \"SchedulingFence\";\n";
out << " default:\n";
out << " return \"INVALID\";\n";
out << " }\n";
out << "}\n\n";
// Generate getOpcodeMnemonic function
out << "const char* getOpcodeMnemonic(Opcode opcode)\n";
out << "{\n";
out << " switch (opcode)\n";
out << " {\n";
out << " case UNKNOWN:\n";
out << " return \"unknown\";\n";
for (const auto& inst : getIRInstructions()) {
out << " case " << inst.className << ":\n";
out << " return \"" << classNameToParseName(inst.className) << "\";\n";
}
// Special opcodes (not auto-generated)
out << " case MFMA:\n";
out << " return \"mfma\";\n";
out << " case MXMFMA:\n";
out << " return \"mxmfma\";\n";
out << " case SMFMA:\n";
out << " return \"smfma\";\n";
out << " case Label:\n";
out << " return \"label\";\n";
out << " case IntrinsicCall:\n";
out << " return \"intrinsic_call\";\n";
out << " case SWaitAlu:\n";
out << " return \"s_wait_alu\";\n";
out << " case SchedulingFence:\n";
out << " return \"scheduling_fence\";\n";
out << " default:\n";
out << " return \"invalid\";\n";
out << " }\n";
out << "}\n\n";
out << "} // namespace logical\n";
out << "} // namespace stinkytofu\n";
std::cout << "Generated opcode mapping functions -> LogicalOpcode.cpp\n";
return true;
}
static bool genIRClasses(const std::string& outdir) {
std::ofstream out(outdir + "/stinkytofu/ir/logical/LogicalInstructions_generated.hpp");
if (!out) {
std::cerr << "Failed to open LogicalInstructions_generated.hpp for writing\n";
return false;
}
// File header with header guards
out << "/* ************************************************************************\n";
out << " * Copyright (C) 2025-2026 Advanced Micro Devices, Inc.\n";
out << " *\n";
out << " * Permission is hereby granted, free of charge, to any person obtaining a copy\n";
out << " * of this software and associated documentation files (the \"Software\"), to "
"deal\n";
out << " * in the Software without restriction, including without limitation the rights\n";
out << " * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n";
out << " * copies of the Software, and to permit persons to whom the Software is\n";
out << " * furnished to do so, subject to the following conditions:\n";
out << " *\n";
out << " * The above copyright notice and this permission notice shall be included in\n";
out << " * all copies or substantial portions of the Software.\n";
out << " *\n";
out << " * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n";
out << " * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n";
out << " * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n";
out << " * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n";
out << " * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n";
out << " * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n";
out << " * THE SOFTWARE.\n";
out << " *\n";
out << " * ************************************************************************ */\n\n";
out << "// Auto-generated by TableGen - DO NOT EDIT\n";
out << "// This file contains high-level IR instruction class definitions\n\n";
out << "#pragma once\n\n";
out << "#include \"stinkytofu/ir/asm/StinkyAsmIR.hpp\"\n";
out << "#include \"stinkytofu/ir/asm/StinkyModifiers.hpp\"\n";
out << "#include \"stinkytofu/core/stinkytofu.hpp\"\n";
out << "#include <iostream>\n";
out << "#include <optional>\n";
out << "#include <string>\n";
out << "\n// TODO: High-level IR should not depend on assembly-level IR "
"(StinkyAsmIR.hpp).\n";
out << "// Extract StinkyRegister and RegType into a separate header (e.g., "
"stinkytofu/ir/StinkyRegister.hpp)\n";
out << "// to fix the inverted dependency. StinkyRegister is a shared primitive used by "
"both\n";
out << "// high-level IR (LogicalInstruction) and assembly IR (StinkyInstruction).\n";
out << "#include <vector>\n\n";
out << "namespace stinkytofu\n";
out << "{\n\n";
out << " // NOTE: LogicalInstruction base class must be defined before including this "
"file\n\n";
std::string currentCategory = "";
for (const auto& inst : getIRInstructions()) {
if (inst.category != currentCategory) {
if (!currentCategory.empty()) {
out << "\n";
}
out << " // "
"========================================================================\n";
out << " // " << inst.category << "\n";
out << " // "
"========================================================================"
"\n\n";
currentCategory = inst.category;
}
// Generate factory function instead of subclass
out << " /**\n";
out << " * @brief Factory function for " << inst.comment << "\n";
out << " */\n";
out << " inline LogicalInstruction* " << inst.className << "(";
// Function parameters
if (inst.hasDest) {
out << "const StinkyRegister& dst";
if (inst.numSrcs > 0) out << ",\n" << std::string(inst.className.length() + 12, ' ');
}
for (int i = 0; i < inst.numSrcs; ++i) {
if (i > 0 || inst.hasDest) {
// Optional sources use pointer with default nullptr
if (i >= inst.numRequiredSrcs) {
out << "const StinkyRegister* src" << i << " = nullptr";
} else {
out << "const StinkyRegister& src" << i;
}
if (i < inst.numSrcs - 1)
out << ",\n" << std::string(inst.className.length() + 12, ' ');
} else {
// First source (no dest case)
if (i >= inst.numRequiredSrcs) {
out << "const StinkyRegister* src" << i << " = nullptr";
} else {
out << "const StinkyRegister& src" << i;
}
if (i < inst.numSrcs - 1)
out << ",\n" << std::string(inst.className.length() + 12, ' ');
}
}
if (inst.hasDest || inst.numSrcs > 0) {
out << ",\n" << std::string(inst.className.length() + 12, ' ');
}
// Add modifier parameters
if (inst.supportsDPP) {
out << "std::optional<DPPModifiers> dpp = std::nullopt,\n"
<< std::string(inst.className.length() + 12, ' ');
}
if (inst.supportsSDWA) {
out << "std::optional<SDWAModifiers> sdwa = std::nullopt,\n"
<< std::string(inst.className.length() + 12, ' ');
}
if (inst.hasDS) {
out << "std::optional<DSModifiers> ds = std::nullopt,\n"
<< std::string(inst.className.length() + 12, ' ');
}
out << "const std::string& comment = \"\")\n";
out << " {\n";
// Function body: create LogicalInstruction with opcode
out << " auto* inst = IRBase::createIR<LogicalInstruction>(logical::"
<< inst.className << ");\n";
// Set destinations
if (inst.hasDest) {
out << " inst->dests.push_back(dst);\n";
}
// Set sources (required and optional)
for (int i = 0; i < inst.numSrcs; ++i) {
if (i >= inst.numRequiredSrcs) {
// Optional source: check if pointer is non-null before adding
out << " if(src" << i << ") inst->srcs.push_back(*src" << i << ");\n";
} else {
// Required source: always add
out << " inst->srcs.push_back(src" << i << ");\n";
}
}
// Set modifiers
if (inst.supportsDPP) {
out << " inst->dpp = std::move(dpp);\n";
}
if (inst.supportsSDWA) {
out << " inst->sdwa = std::move(sdwa);\n";
}
if (inst.hasDS) {
out << " inst->ds = std::move(ds);\n";
}
// Set comment
out << " inst->comment = comment;\n";
// Set flags (parse from flags string)
if (!inst.flags.empty()) {
out << " // Set instruction flags\n";
out << " InstFlagSet flags;\n";
// Split flags by '|' and generate set() calls
std::string flagsStr = inst.flags;
size_t pos = 0;
while ((pos = flagsStr.find('|')) != std::string::npos) {
std::string flag = flagsStr.substr(0, pos);
// Trim whitespace
flag.erase(0, flag.find_first_not_of(" \t"));
flag.erase(flag.find_last_not_of(" \t") + 1);
if (!flag.empty()) {
out << " flags.set(" << flag << ");\n";
}
flagsStr.erase(0, pos + 1);
}
// Handle last flag
flagsStr.erase(0, flagsStr.find_first_not_of(" \t"));
flagsStr.erase(flagsStr.find_last_not_of(" \t") + 1);
if (!flagsStr.empty()) {
out << " flags.set(" << flagsStr << ");\n";
}
out << " inst->setFlags(flags);\n";
}
out << " return inst;\n";
out << " }\n\n";
}
// Generate special MFMA classes
genSpecialMFMAClasses(out);
// Generate helper implementations
out << "\n// "
"============================================================================\n";
out << "// Helper implementations (auto-generated)\n";
out << "// "
"============================================================================\n\n";
// Note: isCommutative() is now inline in LogicalInstructions.hpp using
// flags_.test(IF_Commutative)
// Generate isComposite() implementation
// Note: Composite instructions are determined by CompositeInstructionLoweringPass
out << "inline bool LogicalInstruction::isComposite() const\n";
out << "{\n";
out << " switch (opcode_) {\n";
out << " case logical::VAddPKF32:\n";
out << " case logical::VMulPKF32:\n";
out << " case logical::VMovB64:\n";
out << " case logical::VLShiftLeftOrB32:\n";
out << " case logical::SAddU64:\n";
out << " case logical::VAddNCU64:\n";
out << " case logical::VAddLShiftLeftU32:\n";
out << " case logical::VLShiftLeftAddU32:\n";
out << " return true;\n";
out << " default:\n";
out << " return false;\n";
out << " }\n";
out << "}\n\n";
// Close namespace
out << "} // namespace stinkytofu\n";
std::cout << "Generated " << getIRInstructions().size()
<< " LogicalInstruction factory functions + 5 special instruction factories "
"(MFMA/MXMFMA/SMFMA/Label/IntrinsicCall) -> "
"LogicalInstructions_generated.hpp\n";
return true;
}
// Generate builder method forward declarations
// Generate builder method implementations
// Generate C++ factory functions for all IR instructions
// Generate Python bindings for all IR instructions
static bool genPythonBindings(const std::string& outdir) {
std::ofstream out(outdir + "/PythonBindings_generated.inc");
if (!out) {
std::cerr << "Failed to open PythonBindings_generated.inc for writing\n";
return false;
}
out << "// Auto-generated Python bindings for IR instructions\n";
out << "// DO NOT EDIT - Generated by GenLogicalIR.cpp\n\n";
int count = 0;
// Generate bindings for all regular IR instructions
for (const auto& inst : getIRInstructions()) {
std::string className = inst.className;
// Generate factory function instead of constructor
out << " m.def(\"" << className << "\", [](";
// Constructor parameter types
std::vector<std::string> paramTypes;
std::vector<std::string> paramNames;
std::vector<std::string> defaults;
// Add destination register (if instruction has one)
if (inst.hasDest) {
paramTypes.push_back("const StinkyRegister&");
paramNames.push_back("dest");
defaults.push_back("");
}
// Add source operands (required and optional)
for (int i = 0; i < inst.numSrcs; i++) {
if (i >= inst.numRequiredSrcs) {
// Optional source: use std::optional wrapper for Python
paramTypes.push_back("std::optional<StinkyRegister>");
paramNames.push_back("src" + std::to_string(i));
defaults.push_back("std::nullopt");
} else {
// Required source: regular reference
paramTypes.push_back("const StinkyRegister&");
paramNames.push_back("src" + std::to_string(i));
defaults.push_back("");
}
}
// Add optional modifiers
if (inst.supportsDPP && inst.supportsSDWA) {
paramTypes.push_back("std::optional<DPPModifiers>");
paramNames.push_back("dpp");
defaults.push_back("std::nullopt");
paramTypes.push_back("std::optional<SDWAModifiers>");
paramNames.push_back("sdwa");
defaults.push_back("std::nullopt");
} else if (inst.hasDS) {
paramTypes.push_back("std::optional<DSModifiers>");
paramNames.push_back("ds");
defaults.push_back("std::nullopt");
}
// Add comment parameter
paramTypes.push_back("const std::string&");
paramNames.push_back("comment");
defaults.push_back("\"\"");
// Output lambda parameter list
for (size_t i = 0; i < paramTypes.size(); i++) {
out << paramTypes[i] << " " << paramNames[i];
if (i + 1 < paramTypes.size()) out << ", ";
}
out << ") {\n";
// Return factory function wrapped in shared_ptr (Python-owned: set ownedExternally)
out << " return makeLogicalInstructionShared(" << className << "(";
// Pass parameters, converting optional sources to pointers
for (size_t i = 0; i < paramNames.size(); i++) {
const std::string& paramName = paramNames[i];
// Check if this is an optional source parameter
bool isOptionalSrc = paramName.starts_with("src") &&
paramTypes[i].find("std::optional") != std::string::npos;
if (isOptionalSrc) {
// Convert std::optional<StinkyRegister> to const StinkyRegister*
out << "(" << paramName << ".has_value() ? &" << paramName << ".value() : nullptr)";
} else {
// Regular parameter: pass as-is
out << paramName;
}
if (i + 1 < paramNames.size()) out << ", ";
}
out << "));\n";
out << " },\n";
// Output parameter names and defaults
for (size_t i = 0; i < paramNames.size(); i++) {
out << " nb::arg(\"" << paramNames[i] << "\")";
if (!defaults[i].empty()) out << " = " << defaults[i];
if (i + 1 < paramNames.size()) out << ",\n";
}
out << ",\n";
// Add docstring
out << " \"Create a " << classNameToParseName(inst.className) << " instruction\");\n\n";
count++;
}
std::cout << "Generated Python bindings for " << count
<< " IR instructions -> PythonBindings_generated.inc\n";
return true;
}
// Generate all high-level IR artifacts
// NOLINTNEXTLINE(misc-use-internal-linkage)
bool genLogicalIR(const std::string& outdir) {
bool success = true;
std::cout << "\n=== Generating High-Level IR ===\n";
success &= genOpcodeEnum(outdir);
success &= genOpcodeMappings(outdir);
success &= genIRClasses(outdir);
// Obsolete: genBuilderDecls, genBuilderImpls, genFactoryFunctions
// These are replaced by inline factory functions in LogicalInstructions_generated.hpp
success &= genPythonBindings(outdir);
if (success) {
std::cout << "=== High-Level IR generation completed successfully ===\n\n";
} else {
std::cerr << "=== High-Level IR generation failed ===\n\n";
}
return success;
}
} // namespace stinkytofu