#include #include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(std::FILE *out, const char *argv0) { std::println(out, "{} <--hpp|--cpp|--glsl>", argv0); } struct BitRange { std::uint32_t bitCount; std::uint32_t bitOffset; std::uint32_t paddingSize; std::uint32_t paddingValue; }; struct PredefinedValue { std::string name; std::string description; std::uint32_t value; }; struct MicrocodeField { std::string name; std::string description; std::vector bitLayout; std::vector predefinedValues; bool isConditional; bool isUsed = false; }; using Identifier = std::array; struct EncodingCondition { std::string name; pugi::xml_node expression; }; struct Encoding { std::string name; std::string description; std::uint32_t bitCount; Identifier mask; std::vector identifiers; std::vector microcodeFormat; std::vector encodingConditions; }; enum class Signedness { DoesNotApply, Signed, Unsigned, SignedByModifier, }; struct DataFormatField { std::string name; Signedness signedness; std::vector bitLayout; }; enum class DataType { Bits, Integer, Float, Descriptor, }; struct DataFormat { std::string name; std::string description; DataType dataType; std::uint32_t bitCount; std::uint32_t componentCount; std::vector> fields; bool isUsed = false; }; struct OperandType { std::string name; std::string description; std::vector microcodeFormat; std::vector predefinedValues; bool isUsed = false; bool isNotSideEffect = false; }; struct Operand { bool isInput; bool isOutput; bool isImplicit; bool IsBinaryMicrocodeRequired; std::string fieldName; std::string dataFormatName; std::string type; std::uint32_t size; }; struct InstructionEncoding { std::string name; std::string condition; std::uint32_t opcode; std::vector operands; }; struct Instruction { bool isBranch; bool IsConditionalBranch; bool IsIndirectBranch; bool IsProgramTerminator; bool IsImmediatelyExecuted; std::string name; std::string description; std::vector encodings; std::string functionalGroup; std::string functionalSubgroup; }; struct Isa { std::string archName; std::vector encodings; std::vector dataFormats; std::vector instructions; std::vector operandTypes; std::map functionalGroups; std::vector functionalSubgroups; std::vector encodingGroups; std::map encodingSubgroups; }; static std::string toLower(std::string_view string) { return std::ranges::transform_view(string, [](char c) { return ::tolower(c); }) | std::ranges::to(); } static std::uint32_t getLayoutBitCount(std::span bitLayout) { std::uint32_t result = 0; for (auto range : bitLayout) { result += range.bitCount + range.paddingSize; } return result; } static Identifier parseIdentifier(pugi::xml_node node, std::uint32_t bitCount) { auto radix = node.attribute("Radix").as_int(10); auto value = std::string_view(node.text().as_string()); auto it = value.data(); auto end = value.data() + value.size(); auto wordCount = ((bitCount + 7) / 8 + 3) / 4; Identifier result; if (wordCount > result.size()) { std::println(stderr, "out of identifier limit, {}", bitCount); std::abort(); } std::uint32_t count = 0; while (it != end) { if (count >= result.size()) { std::println(stderr, "out of identifier limit, {}", bitCount); std::abort(); } auto last = end; if (radix == 2) { last = std::min(it + 32, end); } std::uint32_t word; auto [ptr, ec] = std::from_chars(it, last, word, radix); if (ec != std::errc{}) { std::println(stderr, "failed to parse identifier {}", value); std::abort(); } result[wordCount - ++count] = word; it = ptr; } if (count != wordCount) { std::println(stderr, "failed to parse value {}, radix {}, bit count {}, parsed " "words {}, expected words {}", value, radix, bitCount, count, wordCount); std::abort(); } return result; } static std::vector parseBitLayout(pugi::xml_node xml) { std::vector result; for (auto range : xml.children("Range")) { BitRange newRange; newRange.bitCount = range.child("BitCount").text().as_int(); newRange.bitOffset = range.child("BitOffset").text().as_int(); if (auto padding = range.child("Padding")) { newRange.paddingSize = padding.child("BitCount").text().as_int(); newRange.paddingValue = parseIdentifier(padding.child("Value"), newRange.paddingSize)[0]; } else { newRange.paddingSize = 0; newRange.paddingValue = 0; } result.push_back(newRange); } return result; } static PredefinedValue parsePredefinedValue(pugi::xml_node xml) { PredefinedValue result; result.name = xml.child_value("Name"); result.description = xml.child_value("Description"); result.value = xml.child("Value").text().as_int(); return result; } static MicrocodeField parseMicrocodeField(pugi::xml_node xml) { MicrocodeField result; result.name = xml.child_value("FieldName"); result.description = xml.child_value("Description"); result.bitLayout = parseBitLayout(xml.child("BitLayout")); if (auto predefinedValues = xml.child("FieldPredefinedValues")) { for (auto predefinedValue : predefinedValues.children("PredefinedValue")) { result.predefinedValues.push_back(parsePredefinedValue(predefinedValue)); } } result.isConditional = xml.attribute("IsConditional").as_bool(); return result; } static Encoding parseEncoding(pugi::xml_node xml) { Encoding result; result.name = xml.child_value("EncodingName"); result.description = xml.child_value("Description"); result.bitCount = xml.child("BitCount").text().as_int(); result.mask = parseIdentifier(xml.child("EncodingIdentifierMask"), result.bitCount); for (auto ident : xml.child("EncodingIdentifiers").children("EncodingIdentifier")) { result.identifiers.push_back(parseIdentifier(ident, result.bitCount)); } for (auto field : xml.child("MicrocodeFormat").child("BitMap").children("Field")) { result.microcodeFormat.push_back(parseMicrocodeField(field)); } if (auto conditions = xml.child("EncodingConditions")) { for (auto condition : conditions.children("EncodingCondition")) { result.encodingConditions.push_back({ .name = condition.child_value("ConditionName"), .expression = condition.child("CondtionExpression").child("Expression"), }); } } return result; } static DataFormatField parseDataFormatField(pugi::xml_node xml) { DataFormatField result; result.name = xml.child_value("FieldName"); auto signedness = std::string_view(xml.attribute("Signedness").as_string()); if (signedness == "DoesNotApply") { result.signedness = Signedness::DoesNotApply; } else if (signedness == "Signed") { result.signedness = Signedness::Signed; } else if (signedness == "Unsigned") { result.signedness = Signedness::Unsigned; } else if (signedness == "SignedByModifier") { result.signedness = Signedness::SignedByModifier; } else { std::println(stderr, "unknown data format field signedness {}", signedness); std::abort(); } result.bitLayout = parseBitLayout(xml.child("BitLayout")); return result; } static DataFormat parseDataFormat(pugi::xml_node xml) { DataFormat result{}; result.name = xml.child_value("DataFormatName"); result.description = xml.child_value("Description"); auto dataType = std::string_view(xml.child_value("DataType")); if (dataType == "bits") { result.dataType = DataType::Bits; } else if (dataType == "integer") { result.dataType = DataType::Integer; } else if (dataType == "float") { result.dataType = DataType::Float; } else if (dataType == "descriptor") { result.dataType = DataType::Descriptor; } else { std::println(stderr, "unknown data format type {}", dataType); std::abort(); } result.bitCount = xml.child("BitCount").text().as_int(); result.componentCount = xml.child("ComponentCount").text().as_int(); for (auto componentFields : xml.child("DataAttributes").children("BitMap")) { auto &resultFields = result.fields.emplace_back(); for (auto field : componentFields.children("Field")) { resultFields.push_back(parseDataFormatField(field)); } } return result; } static Operand parseOperand(pugi::xml_node xml) { Operand result; result.isInput = xml.attribute("Input").as_bool(); result.isOutput = xml.attribute("Output").as_bool(); result.isImplicit = xml.attribute("Implicit").as_bool(); result.IsBinaryMicrocodeRequired = xml.attribute("BinaryMicrocodeRequired").as_bool(); result.fieldName = xml.child_value("FieldName"); result.dataFormatName = xml.child_value("DataFormatName"); result.type = xml.child_value("OperandType"); result.size = xml.child("OperandSize").text().as_int(); return result; } static InstructionEncoding parseInstructionEncoding(pugi::xml_node xml) { InstructionEncoding result; result.name = xml.child_value("EncodingName"); result.condition = xml.child_value("EncodingCondition"); result.opcode = parseIdentifier(xml.child("Opcode"), 32)[0]; for (auto operand : xml.child("Operands").children("Operand")) { auto parsed = parseOperand(operand); auto it = std::ranges::find_if(result.operands, [&](const Operand &exists) { return exists.size == parsed.size && exists.type == parsed.type && exists.dataFormatName == parsed.dataFormatName && exists.fieldName == parsed.fieldName; }); if (it != result.operands.end()) { if (parsed.isInput) { it->isInput = true; } if (parsed.isOutput) { it->isOutput = true; } if (!parsed.isImplicit) { it->isImplicit = false; } if (parsed.IsBinaryMicrocodeRequired) { it->IsBinaryMicrocodeRequired = true; } continue; } result.operands.push_back(parsed); } return result; } static Instruction parseInstruction(pugi::xml_node xml) { Instruction result; auto flags = xml.child("InstructionFlags"); result.isBranch = flags.child("IsBranch").text().as_bool(); result.IsConditionalBranch = flags.child("IsConditionalBranch").text().as_bool(); result.IsIndirectBranch = flags.child("IsIndirectBranch").text().as_bool(); result.IsProgramTerminator = flags.child("IsProgramTerminator").text().as_bool(); result.IsImmediatelyExecuted = flags.child("IsImmediatelyExecuted").text().as_bool(); result.name = xml.child_value("InstructionName"); result.description = xml.child_value("Description"); for (auto encoding : xml.child("InstructionEncodings").children("InstructionEncoding")) { result.encodings.push_back(parseInstructionEncoding(encoding)); } auto funcGroup = xml.child("FunctionalGroup"); result.functionalGroup = funcGroup.child_value("Name"); result.functionalSubgroup = funcGroup.child_value("Subgroup"); return result; } static OperandType parseOperandType(pugi::xml_node xml) { OperandType result; result.name = xml.child_value("OperandTypeName"); result.description = xml.child_value("Description"); if (xml.attribute("IsPartitioned").as_bool()) { for (auto field : xml.child("MicrocodeFormat").child("BitMap").children("Field")) { result.microcodeFormat.push_back(parseMicrocodeField(field)); } } else { for (auto predefinedValue : xml.child("OperandPredefinedValues").children("PredefinedValue")) { result.predefinedValues.push_back(parsePredefinedValue(predefinedValue)); } } return result; } static Isa parseIsa(pugi::xml_node xml) { Isa result; result.archName = xml.child("Architecture").child_value("ArchitectureName"); if (result.archName.starts_with("AMD ")) { result.archName = result.archName.substr(4); } result.archName = toLower(result.archName); for (auto pos = result.archName.find(' '); pos != std::string::npos; pos = result.archName.find(' ')) { result.archName.erase(pos, 1); } for (auto encoding : xml.child("Encodings").children("Encoding")) { result.encodings.push_back(parseEncoding(encoding)); } for (auto instruction : xml.child("Instructions").children("Instruction")) { result.instructions.push_back(parseInstruction(instruction)); } for (auto dataFormat : xml.child("DataFormats").children("DataFormat")) { result.dataFormats.push_back(parseDataFormat(dataFormat)); } for (auto operandType : xml.child("OperandTypes").children("OperandType")) { result.operandTypes.push_back(parseOperandType(operandType)); } for (auto group : xml.child("FunctionalGroups").children("FunctionalGroup")) { auto name = group.child_value("Name"); auto description = group.child_value("Description"); result.functionalGroups[name] = description; } for (auto group : xml.child("FunctionalSubgroups").children("FunctionalSubgroup")) { result.functionalSubgroups.emplace_back(group.child_value("Name")); } for (auto &encoding : result.encodings) { if (encoding.name.starts_with("ENC_")) { result.encodingGroups.push_back(encoding.name.substr(4)); } else { result.encodingSubgroups[encoding.name] = encoding.name.substr(0, encoding.name.find('_')); } } for (auto &inst : result.instructions) { for (auto &instEnc : inst.encodings) { std::string baseEncoding; if (instEnc.name.starts_with("ENC_")) { baseEncoding = instEnc.name.substr(4); } else { baseEncoding = result.encodingSubgroups.at(instEnc.name); } auto baseEncIt = std::ranges::find(result.encodings, std::string_view(baseEncoding), [](const Encoding &enc) { return std::string_view(enc.name).substr(4); }); if (baseEncIt == result.encodings.end()) { std::println(stderr, "instruction {} references to undefined base encoding {}", inst.name, instEnc.name); std::abort(); } for (auto &op : instEnc.operands) { auto dataFormatIt = std::ranges::find( result.dataFormats, std::string_view(op.dataFormatName), [](const DataFormat &format) { return std::string_view(format.name); }); if (dataFormatIt == result.dataFormats.end()) { std::println(stderr, "instruction {} references to undefined data format {}", inst.name, op.dataFormatName); std::abort(); } auto typeIt = std::ranges::find(result.operandTypes, std::string_view(op.type), [](const OperandType &type) { return std::string_view(type.name); }); if (typeIt == result.operandTypes.end()) { std::println(stderr, "instruction {} references to undefined type {}", inst.name, op.type); std::abort(); } dataFormatIt->isUsed = true; typeIt->isUsed = true; if (!op.fieldName.empty()) { typeIt->isNotSideEffect = true; } else if (!op.isImplicit && op.type == "OPR_SIMM32") { std::println( stderr, "explicit SIMM32 operand without field name, instruction " "{}, type {}", inst.name, typeIt->name); op.fieldName = "SIMM32"; typeIt->isNotSideEffect = true; } if (!op.fieldName.empty()) { auto microcodeFieldIt = std::ranges::find( baseEncIt->microcodeFormat, std::string_view(op.fieldName), [](MicrocodeField µcode) { return std::string_view(microcode.name); }); if (microcodeFieldIt != baseEncIt->microcodeFormat.end()) { microcodeFieldIt->isUsed = true; } } } } } return result; } static void printComment(std::string_view text, int level) { while (!text.empty()) { auto eolPos = text.find('\n'); auto line = text.substr(0, eolPos); if (!line.empty()) { std::println("{}// {}", std::string(level * 2, ' '), line); } if (eolPos == std::string_view::npos) { break; } text = text.substr(eolPos + 1); } } static std::string emitVariableRead(std::uint32_t resultBitSize, std::string_view variableName, std::uint32_t variableBitSize, BitRange range, bool isGlsl = false) { auto result = std::string(variableName); bool requiresParens = false; if (range.bitOffset != 0) { result += std::format(" >> {}", range.bitOffset); requiresParens = true; } if (resultBitSize != range.bitCount || resultBitSize == 1) { if (requiresParens) { result = std::format("({})", result); } result += std::format(" & {:#x}", (1ull << range.bitCount) - 1); requiresParens = true; } if (resultBitSize == 1) { if (range.paddingSize != 0) { std::println(stderr, "unexpected padding for boolean value"); std::abort(); } if (requiresParens) { result = std::format("({})", result); } return std::format("{} != 0", result); } if (range.paddingSize) { if (requiresParens) { result = std::format("({})", result); } result = std::format("{} << {}", result, range.paddingSize); if (range.paddingValue != 0) { result = std::format("({}) | {:#x}", result, range.paddingValue); } } if (resultBitSize != variableBitSize) { if (isGlsl) { return std::format("uint{}_t({})", resultBitSize, result); } return std::format("static_cast({})", resultBitSize, result); } return result; } static std::string emitArrayRead(std::uint32_t resultBitSize, std::string_view arrayName, std::uint32_t itemBitSize, BitRange range) { auto elementIndex = range.bitOffset / itemBitSize; range.bitOffset %= itemBitSize; return emitVariableRead(resultBitSize, std::format("{}[{}]", arrayName, elementIndex), itemBitSize, range); } static std::string emitExpression(pugi::xml_node xml) { auto type = xml.attribute("Type").as_string(); if (type == std::string_view("Operator")) { auto op = xml.child_value("Operator"); if (op == std::string_view(".fieldderef")) { auto subexpressions = xml.child("Subexpressions").children("Expression"); auto exprs = std::vector(subexpressions.begin(), subexpressions.end()); auto it = subexpressions.begin(); if (it->child_value("Label") == std::string_view("INST")) { return ""; } if (exprs.size() != 3) { std::println(stderr, "unexpected .fieldderef subexpression count, {}", exprs.size()); std::abort(); } auto fieldSize = exprs[2].child("ValueType").child("Size").text().as_int(); if (fieldSize == 1) { return std::string("is") + exprs[1].child_value("Label") + "()"; } return std::string("get") + exprs[1].child_value("Label") + "()"; } auto subexpressions = xml.child("Subexpressions").children("Expression"); auto exprs = std::vector(subexpressions.begin(), subexpressions.end()); if (exprs.size() != 3 && exprs.size() != 2) { std::println(stderr, "unexpected {} subexpression count, {}", op, exprs.size()); std::abort(); } return std::format("({} {} {})", emitExpression(exprs[0]), op, emitExpression(exprs[1])); } if (type == std::string_view("Literal")) { auto value = std::string_view(xml.child_value("Value")); if (xml.child("ValueType").child("Size").text().as_int() == 1) { return value == "0" ? "false" : "true"; } return std::string(value); } std::println(stderr, "unexpected expression type {}", type); std::abort(); } enum class OutputType { Header, Source, Glsl }; int main(int argc, const char *argv[]) { if (argc < 3) { if (argc == 2) { if (argv[1] == std::string_view("-h") || argv[1] == std::string_view("--help")) { usage(stdout, argv[0]); return 0; } } usage(stderr, argv[0]); return 1; } OutputType type; if (argv[2] == std::string_view("--glsl")) { type = OutputType::Glsl; } else if (argv[2] == std::string_view("--hpp")) { type = OutputType::Header; } else if (argv[2] == std::string_view("--cpp")) { type = OutputType::Source; } else { usage(stderr, argv[0]); return 1; } pugi::xml_document doc; if (!doc.load_file(argv[1])) { std::println(stderr, "failed to load {}", argv[1]); return 1; } auto isa = parseIsa(doc.child("Spec").child("ISA")); struct EncodingParamField { std::string name; std::uint32_t bitCount; std::uint32_t bitOffset; std::uint32_t typeSize; }; struct EncodingParams { std::uint32_t typeSize; std::vector fields; }; std::map encodingParams; std::map> groupToInstructionSet; for (auto &inst : isa.instructions) { for (auto &instEnc : inst.encodings) { auto group = instEnc.name; if (group.starts_with("ENC_")) { group = group.substr(4); } else { group = isa.encodingSubgroups.at(instEnc.name); } auto &groupSet = groupToInstructionSet[group]; auto [it, inserted] = groupSet.insert({instEnc.opcode, inst.name}); if (!inserted) { if (inst.name != it->second) { std::println( stderr, "encoding group changes instruction instruction {}, encoding {}", inst.name, instEnc.name); } } } } for (auto &enc : isa.encodings) { if (!enc.name.starts_with("ENC_")) { continue; } std::vector params; std::uint64_t totalFlagsSize = 0; for (auto &field : enc.microcodeFormat) { if (field.name == "ENCODING" || field.name == "OP" || field.name == "OPM") { continue; } if (!field.isUsed) { params.emplace_back(field); totalFlagsSize += getLayoutBitCount(field.bitLayout); } } if (!params.empty()) { auto &encParams = encodingParams[enc.name.substr(4)]; std::uint32_t storageTypeSize; if (totalFlagsSize == 1) { storageTypeSize = 1; } else if (totalFlagsSize > 32) { storageTypeSize = 64; } else if (totalFlagsSize > 16) { storageTypeSize = 32; } else if (totalFlagsSize > 8) { storageTypeSize = 16; } else { storageTypeSize = 8; } encParams.typeSize = storageTypeSize; std::uint32_t bitOffset = 0; for (auto ¶m : params) { auto totalBitCount = getLayoutBitCount(param.bitLayout); if (totalBitCount == 1) { encParams.fields.emplace_back(EncodingParamField{ .name = param.name, .bitCount = 1, .bitOffset = bitOffset, .typeSize = 1, }); bitOffset += 1; } else { std::uint32_t fieldTypeSize; if (totalBitCount > 32) { fieldTypeSize = 64; } else if (totalBitCount > 16) { fieldTypeSize = 32; } else if (totalBitCount > 8) { fieldTypeSize = 16; } else { fieldTypeSize = 8; } encParams.fields.emplace_back(EncodingParamField{ .name = param.name, .bitCount = totalBitCount, .bitOffset = bitOffset, .typeSize = fieldTypeSize, }); bitOffset += totalBitCount; } } } } if (type == OutputType::Source || type == OutputType::Header) { if (type == OutputType::Header) { std::println("#pragma once"); } else { std::println("#include \"{}.hpp\"", isa.archName); } std::println("#include "); std::println("#include "); std::println("#include "); std::println("#include "); if (type == OutputType::Source) { std::println("#include "); } std::println(""); } if (type == OutputType::Header) { std::println("namespace amdgpu::{} {{", isa.archName); std::println(""); std::println("enum class OperandAccess : std::uint8_t {{"); std::println(" None,"); std::println(" Read,"); std::println(" Write,"); std::println(" ReadWrite,"); std::println("}};"); std::println(""); std::println("enum class InstructionFlags : std::uint8_t {{"); std::println(" None = 0,"); std::println(" Branch = 1 << 0,"); std::println(" ConditionalBranch = 1 << 1,"); std::println(" IndirectBranch = 1 << 2,"); std::println(" ProgramTerminator = 1 << 3,"); std::println(" ImmediatelyExecuted = 1 << 4,"); std::println("}};"); std::println("constexpr inline InstructionFlags operator|(InstructionFlags " "lhs, InstructionFlags rhs) {{"); std::println( " return static_cast(static_cast(lhs) " "| static_cast(rhs));"); std::println("}}"); std::println("constexpr inline InstructionFlags operator&(InstructionFlags " "lhs, InstructionFlags rhs) {{"); std::println( " return static_cast(static_cast(lhs) " "& static_cast(rhs));"); std::println("}}"); std::println(""); std::println("enum class InstructionKind : std::uint8_t {{"); for (auto &kind : isa.encodingGroups) { std::println(" {},", kind); } std::println("}};"); std::println(""); std::println("enum class OperandType : std::uint8_t {{"); for (auto &type : isa.operandTypes) { if (!type.isUsed) { continue; } auto name = type.name; if (name.starts_with("OPR_")) { name = name.substr(4); } if (type.description.empty()) { std::println(" {},", name); } else if (type.description.find('\n') != std::string::npos) { printComment(type.description, 1); std::println(" {},", name); } else { std::print(" {}, ", name); printComment(type.description, 0); } } std::println("}};"); std::println(""); std::println("enum class DataFormat : std::uint8_t {{"); for (auto &format : isa.dataFormats) { if (!format.isUsed) { continue; } auto name = format.name; if (name.starts_with("FMT_")) { name = name.substr(4); } if (format.description.empty()) { std::println(" {},", name); } else if (format.description.find('\n') != std::string::npos) { printComment(format.description, 1); std::println(" {},", name); } else { std::print(" {}, ", name); printComment(format.description, 0); } } std::println("}};"); for (auto &type : isa.operandTypes) { if (!type.isUsed || !type.isNotSideEffect) { continue; } printComment(type.description, 0); if (!type.predefinedValues.empty()) { std::println(""); std::println("enum class {} {{", type.name); for (auto &value : type.predefinedValues) { std::string name = value.name; if (name == "0.15915494") { name = "r2p"; } else if (name[0] == '-') { name[0] = '_'; name.insert(1, "m"); } else if (name[0] >= '0' && name[0] <= '9') { name.insert(0, "_"); } if (auto dotPos = name.find('.'); dotPos != std::string::npos) { name[dotPos] = '_'; } if (value.description.empty()) { std::println(" {} = {},", name, value.value); } else if (value.description.find('\n') != std::string::npos) { printComment(value.description, 1); std::println(" {} = {},", name, value.value); } else { std::print(" {} = {}, ", name, value.value); printComment(value.description, 0); } } std::println("}};"); continue; } if (type.microcodeFormat.empty()) { continue; } std::println(""); std::println("struct {} {{", type.name); std::uint32_t layoutSize = 0; for (auto µcode : type.microcodeFormat) { for (auto bitRange : microcode.bitLayout) { layoutSize = std::max(layoutSize, bitRange.bitOffset + bitRange.bitCount); } } if (layoutSize > 0) { std::uint32_t partitionTypeSize; if (layoutSize > 64) { std::println(stderr, "too big layout {}", layoutSize); std::abort(); } else if (layoutSize > 32) { partitionTypeSize = 64; } else if (layoutSize > 16) { partitionTypeSize = 32; } else if (layoutSize > 8) { partitionTypeSize = 16; } else { partitionTypeSize = 8; } std::println(" std::uint{}_t raw;", partitionTypeSize); for (auto µcode : type.microcodeFormat) { auto totalBitCount = getLayoutBitCount(microcode.bitLayout); std::uint32_t typeSize = 32; if (totalBitCount > 64) { std::println(stderr, "too big field {}", totalBitCount); std::abort(); } else if (totalBitCount > 32) { typeSize = 64; } else if (totalBitCount > 16) { typeSize = 32; } else if (totalBitCount > 8) { typeSize = 16; } else if (totalBitCount == 1) { typeSize = 1; } else { typeSize = 8; } if (!microcode.predefinedValues.empty()) { if (typeSize == 1) { typeSize = 8; } std::println(""); std::println(" enum class {} : std::uint{}_t {{", microcode.name, typeSize); for (auto &value : microcode.predefinedValues) { std::string name = value.name; if (value.description.empty()) { std::println(" {} = {},", name, value.value); } else if (value.description.find('\n') != std::string::npos) { printComment(value.description, 2); std::println(" {} = {},", name, value.value); } else { std::print(" {} = {}, ", name, value.value); printComment(value.description, 0); } } std::println(" }};"); } std::println(""); printComment(microcode.description, 1); if (typeSize == 1) { if (microcode.bitLayout.size() != 1) { std::abort(); } std::println(" [[nodiscard]] bool is{}() const {{", microcode.name); auto offset = microcode.bitLayout.back().bitOffset; std::println(" return ((raw >> {}) & 1) != 0;", offset); } else { if (microcode.predefinedValues.empty()) { std::println(" [[nodiscard]] std::uint{}_t get{}() const {{", typeSize, microcode.name); } else { std::println(" [[nodiscard]] {} get{}() const {{", microcode.name, microcode.name); } if (microcode.bitLayout.size() == 1) { auto &bitRange = microcode.bitLayout.back(); if (bitRange.bitCount > layoutSize) { std::println(stderr, "unexpected field size {}", bitRange.bitCount); std::abort(); } if (microcode.predefinedValues.empty()) { std::println(" return {};", emitVariableRead(typeSize, "raw", partitionTypeSize, bitRange)); } else { std::println(" return static_cast<{}>({});", microcode.name, emitVariableRead(typeSize, "raw", partitionTypeSize, bitRange)); } } else if (microcode.predefinedValues.empty()) { std::println(" std::uint{}_t result = 0;", typeSize); std::uint32_t resultBitOffset = 0; for (auto bitRange : microcode.bitLayout) { if (bitRange.bitCount > 32) { std::println(stderr, "unexpected field size {}", bitRange.bitCount); std::abort(); } if (resultBitOffset) { std::println(" result |= ({}) << {};", emitVariableRead(typeSize, "raw", partitionTypeSize, bitRange), resultBitOffset); } else { std::println(" result |= {};", emitVariableRead(typeSize, "raw", partitionTypeSize, bitRange)); } resultBitOffset += bitRange.bitCount; } if (microcode.predefinedValues.empty()) { std::println(" return result;"); } else { std::println(" return static_cast<{}>(result);", microcode.name); } } } std::println(" }}"); } } std::println("}};"); } for (auto &enc : isa.encodings) { if (!enc.name.starts_with("ENC_")) { continue; } std::vector params; std::uint64_t totalFlagsSize = 0; for (auto &field : enc.microcodeFormat) { if (field.name == "ENCODING" || field.name == "OP" || field.name == "OPM") { continue; } if (!field.isUsed) { params.emplace_back(field); totalFlagsSize += getLayoutBitCount(field.bitLayout); } } if (!params.empty()) { if (totalFlagsSize > 64) { std::println(stderr, "too big storage for flags"); std::abort(); } std::uint32_t storageTypeSize; if (totalFlagsSize > 32) { storageTypeSize = 64; } else if (totalFlagsSize > 16) { storageTypeSize = 32; } else if (totalFlagsSize > 8) { storageTypeSize = 16; } else { storageTypeSize = 8; } std::println(""); std::println("struct {}_PARAMS {{", enc.name.substr(4)); std::println(" std::uint{}_t raw;", storageTypeSize); std::uint32_t layoutOffset = 0; std::string ctorArgs; std::string ctorBody; for (auto ¶m : params) { auto totalBitCount = getLayoutBitCount(param.bitLayout); std::uint32_t fieldTypeSize; if (totalBitCount > 32) { fieldTypeSize = 64; } else if (totalBitCount > 16) { fieldTypeSize = 32; } else if (totalBitCount > 8) { fieldTypeSize = 16; } else { fieldTypeSize = 8; } if (!ctorArgs.empty()) { ctorArgs += ", "; } std::println(""); printComment(param.description, 1); if (totalBitCount == 1) { if (param.bitLayout.size() != 1) { std::abort(); } ctorArgs += "bool "; ctorArgs += toLower(param.name); ctorBody += std::format(" raw |= ({} ? 1 : 0) << {};\n", toLower(param.name), layoutOffset); std::println(" [[nodiscard]] bool is{}() const {{", param.name); std::println(" return ((raw >> {}) & 1) != 0;", layoutOffset); layoutOffset += 1; } else { std::println(" [[nodiscard]] std::uint{}_t get{}() const {{", fieldTypeSize, param.name); ctorArgs += std::format("std::uint{}_t ", fieldTypeSize); ctorArgs += toLower(param.name); if (param.bitLayout.size() == 1) { auto bitRange = param.bitLayout.back(); bitRange.bitOffset = layoutOffset; if (bitRange.bitCount > storageTypeSize) { std::println(stderr, "unexpected field size {}", bitRange.bitCount); std::abort(); } std::println(" return {};", emitVariableRead(storageTypeSize, "raw", fieldTypeSize, bitRange)); ctorBody += std::format( " raw |= ({} & {:#x}) << {};\n", toLower(param.name), (1ull << bitRange.bitCount) - 1, layoutOffset); layoutOffset += totalBitCount; } else { std::println(" std::uint{}_t result = 0;", fieldTypeSize); std::uint32_t resultBitOffset = 0; for (auto bitRange : param.bitLayout) { bitRange.bitOffset = layoutOffset; if (bitRange.bitCount > 32) { std::println(stderr, "unexpected field size {}", bitRange.bitCount); std::abort(); } if (resultBitOffset) { std::println(" result |= ({}) << {};", emitVariableRead(storageTypeSize, "raw", fieldTypeSize, bitRange), resultBitOffset); } else { std::println(" result |= {};", emitVariableRead(storageTypeSize, "raw", fieldTypeSize, bitRange)); } ctorBody += std::format( " raw |= ({} & {:#x}) << {};\n", toLower(param.name), (1ull << bitRange.bitCount) - 1, layoutOffset); resultBitOffset += bitRange.bitCount; layoutOffset += bitRange.bitCount; } std::println(" return result;"); } } std::println(" }}"); } std::println(""); std::println(" static {}_PARAMS Create({}) {{", enc.name.substr(4), ctorArgs); std::println(" std::uint{}_t raw = 0;", storageTypeSize); std::print("{}", ctorBody); std::println(" return {{.raw = raw}};"); std::println(" }}"); std::println("}};"); } } std::println(""); std::println("struct Operand {{"); std::println(" OperandType type;"); std::println(" OperandAccess access;"); std::println(" DataFormat format;"); std::println(" std::uint32_t size;"); std::println(" union {{"); std::println(" std::uint32_t id;"); for (auto &type : isa.operandTypes) { if (!type.isUsed || type.microcodeFormat.empty()) { continue; } auto fieldName = type.name.substr(4); std::println(" {} {};", type.name, fieldName); } std::println(" }};"); for (auto &type : isa.operandTypes) { if (!type.isUsed) { continue; } auto fieldName = type.name.substr(4); bool isPartitioned = !type.microcodeFormat.empty(); std::println(""); std::print(" static Operand Create{}(OperandAccess access", fieldName); if (type.isNotSideEffect) { if (isPartitioned) { std::uint32_t layoutSize = 0; for (auto µcode : type.microcodeFormat) { for (auto bitRange : microcode.bitLayout) { layoutSize = std::max(layoutSize, bitRange.bitOffset + bitRange.bitCount); } } std::uint32_t partitionTypeSize; if (layoutSize > 32) { partitionTypeSize = 64; } else if (layoutSize > 16) { partitionTypeSize = 32; } else if (layoutSize > 8) { partitionTypeSize = 16; } else { partitionTypeSize = 8; } std::print(", std::uint{}_t raw", partitionTypeSize); } else { std::print( ", DataFormat format, std::uint32_t size, std::uint32_t id"); } } std::println(") {{"); std::println(" Operand result; ", type.name); std::println(" result.type = OperandType::{};", fieldName); std::println(" result.access = access;"); if (type.isNotSideEffect) { if (isPartitioned) { // std::println(" static_assert(sizeof(raw) == sizeof({}));", // type.name); std::println(" std::memcpy(&result.{}, &raw, sizeof(raw));", fieldName); } else { std::println(" result.format = format;"); std::println(" result.size = size;"); std::println(" result.id = id;"); } } std::println(" return result;"); std::println(" }}"); } for (auto &type : isa.operandTypes) { if (!type.isUsed) { continue; } auto fieldName = type.name.substr(4); if (!type.microcodeFormat.empty()) { std::println(" [[nodiscard]] const {} &getAs{}() const {{", type.name, fieldName); std::println(" return {};", fieldName); std::println(" }}"); } else if (type.isNotSideEffect && !type.predefinedValues.empty()) { std::println(" [[nodiscard]] {} getAs{}() const {{", type.name, fieldName); std::println(" return static_cast<{}>(id);", type.name); std::println(" }}"); } } std::println(" [[nodiscard]] bool isSideEffect() const {{"); std::println(" switch (getType()) {{"); for (auto &type : isa.operandTypes) { if (!type.isUsed) { continue; } std::println(" case OperandType::{}: return {};", type.name.substr(4), type.isNotSideEffect ? "false" : "true"); } std::println(" }}"); std::println(" return false;"); std::println(" }}"); std::println(" [[nodiscard]] bool isPartitioned() const {{"); std::println(" switch (getType()) {{"); for (auto &type : isa.operandTypes) { if (!type.isUsed) { continue; } std::println(" case OperandType::{}: return {};", type.name.substr(4), type.isNotSideEffect && !type.microcodeFormat.empty() ? "true" : "false"); } std::println(" }}"); std::println(" return false;"); std::println(" }}"); std::println(" [[nodiscard]] OperandType getType() const {{"); std::println(" return type;"); std::println(" }}"); std::println(" [[nodiscard]] DataFormat getFormat() const {{"); std::println(" return format;"); std::println(" }}"); std::println(" [[nodiscard]] std::uint32_t getSize() const {{"); std::println(" return size;"); std::println(" }}"); std::println(" [[nodiscard]] std::uint32_t getId() const {{"); std::println(" return id;"); std::println(" }}"); std::println(" [[nodiscard]] bool hasR() const {{"); std::println(" return access == OperandAccess::Read || access == " "OperandAccess::ReadWrite;"); std::println(" }}"); std::println(" [[nodiscard]] bool hasW() const {{"); std::println(" return access == OperandAccess::Write || access == " "OperandAccess::ReadWrite;"); std::println(" }}"); std::println(" [[nodiscard]] bool isR() const {{"); std::println(" return access == OperandAccess::Read;"); std::println(" }}"); std::println(" [[nodiscard]] bool isW() const {{"); std::println(" return access == OperandAccess::Write;"); std::println(" }}"); std::println(" [[nodiscard]] bool isRW() const {{"); std::println(" return access == OperandAccess::ReadWrite;"); std::println(" }}"); std::println(" void print(std::ostream &os) const;"); std::println(" void dump() const;"); std::println("}};"); std::uint32_t maxOperandCount = 0; for (auto &inst : isa.instructions) { for (auto &enc : inst.encodings) { if (enc.operands.size() > maxOperandCount) { maxOperandCount = enc.operands.size(); } } } std::println(""); std::println("struct Instruction {{"); std::println(" std::uint16_t opcode;"); std::println(" InstructionKind kind;"); std::println(" std::uint8_t operandCount;"); std::println(" union {{"); for (auto &group : isa.encodingGroups) { if (encodingParams.contains(group)) { std::println(" {0}_PARAMS {0}_params;", group); } } std::println(" }};"); std::println(" Operand operands[{}];", maxOperandCount); std::println(""); for (auto &group : isa.encodingGroups) { std::print(" static Instruction Create{}(std::uint16_t opcode", group); auto paramsIt = encodingParams.find(group); if (paramsIt != encodingParams.end()) { for (auto ¶mField : paramsIt->second.fields) { std::print(", {} {}", paramField.typeSize == 1 ? "bool" : std::format("std::uint{}_t", paramField.typeSize), paramField.name); } } std::println(") {{"); std::println(" Instruction result;"); std::println(" result.opcode = opcode;"); std::println(" result.kind = InstructionKind::{};", group); std::println(" result.operandCount = 0;"); if (paramsIt != encodingParams.end()) { std::print(" result.{0}_params = {0}_PARAMS::Create(", group); for (bool first = true; auto &field : paramsIt->second.fields) { if (first) { first = false; } else { std::print(", "); } std::print("{}", field.name); } std::println(");"); } std::println(" return result;"); std::println(" }}"); std::println(""); } std::println(" void addOperand(Operand operand) {{"); std::println(" operands[operandCount++] = operand;"); std::println(" }}"); std::println(""); std::println( " [[nodiscard, gnu::pure]] InstructionFlags getFlags() const;"); std::println(" [[nodiscard]] bool isBranch() const {{"); std::println(" return (getFlags() & InstructionFlags::Branch) == " "InstructionFlags::Branch;"); std::println(" }}"); std::println(" [[nodiscard]] bool isConditionalBranch() const {{"); std::println( " return (getFlags() & InstructionFlags::ConditionalBranch) == " "InstructionFlags::ConditionalBranch;"); std::println(" }}"); std::println(" [[nodiscard]] bool isIndirectBranch() const {{"); std::println(" return (getFlags() & InstructionFlags::IndirectBranch) " "== InstructionFlags::IndirectBranch;"); std::println(" }}"); std::println(" [[nodiscard]] bool isProgramTerminator() const {{"); std::println( " return (getFlags() & InstructionFlags::ProgramTerminator) == " "InstructionFlags::ProgramTerminator;"); std::println(" }}"); std::println(" [[nodiscard]] bool isImmediatelyExecuted() const {{"); std::println( " return (getFlags() & InstructionFlags::ImmediatelyExecuted) == " "InstructionFlags::ImmediatelyExecuted;"); std::println(" }}"); std::println(" void print(std::ostream &os) const;"); std::println(" void dump() const;"); std::println("}};"); std::println(""); std::println("bool decode(Instruction &instruction, std::span &words);"); std::println("}} // namespace amdgpu::{}", isa.archName); return 0; } if (type == OutputType::Source) { std::println("using namespace amdgpu::{};", isa.archName); for (auto &[group, insts] : groupToInstructionSet) { std::println( "static const char *get{}OpcodeName(std::uint16_t opcode) {{", group); std::println(" switch (opcode) {{"); for (auto &[opcode, name] : insts) { std::println(" case {}: return \"{}\";", opcode, toLower(name)); } std::println(" default:"); std::println(" return nullptr;"); std::println(" }}"); std::println("}}"); } for (auto &encoding : isa.encodings) { std::println(""); printComment(encoding.description, 0); std::println("struct {} {{", encoding.name); auto wordCount = (((encoding.bitCount + 7) / 8) + 3) / 4; std::println(" std::uint32_t raw[{}];", wordCount); std::println(""); std::println(" [[nodiscard]] static bool test(std::uint32_t inst) {{"); int nonZeroWords = 0; for (std::size_t i = 0; i < wordCount; ++i) { if (encoding.mask[i] == 0) { continue; } nonZeroWords++; } if (nonZeroWords == 1 && encoding.mask[0] != 0) { std::println(" switch (inst & 0x{:08x}) {{", encoding.mask[0]); for (auto &ident : encoding.identifiers) { std::println(" case 0x{:08x}:", ident[0]); } std::println(" return true;"); std::println(" default:"); std::println(" return false;"); std::println(" }}"); } else { std::println(stderr, "invalid instruction mask"); std::abort(); } std::println(" }}"); for (auto microcode : encoding.microcodeFormat) { std::println(""); printComment(microcode.description, 1); auto totalBitCount = getLayoutBitCount(microcode.bitLayout); std::uint32_t typeSize = 32; if (totalBitCount > 64) { std::println(stderr, "too big field {}", totalBitCount); std::abort(); } else if (totalBitCount > 32) { typeSize = 64; } else if (totalBitCount > 16) { typeSize = 32; } else if (totalBitCount > 8) { typeSize = 16; } else if (totalBitCount == 1) { typeSize = 1; } else { typeSize = 8; } if (typeSize == 1) { if (microcode.bitLayout.size() != 1) { std::abort(); } std::println(" [[nodiscard]] bool is{}() const {{", microcode.name); auto offset = microcode.bitLayout.back().bitOffset; std::println(" return ((raw[{}] >> {}) & 1) != 0;", offset / 32, offset % 32); } else { std::println(" [[nodiscard]] std::uint{}_t get{}() const {{", typeSize, microcode.name); if (microcode.bitLayout.size() == 1) { auto bitRange = microcode.bitLayout.back(); if (bitRange.bitCount > 32) { std::println(stderr, "unexpected field size {}", bitRange.bitCount); std::abort(); } std::println(" return {};", emitArrayRead(typeSize, "raw", 32, bitRange)); } else { std::println(" std::uint{}_t result = 0;", typeSize); std::uint32_t resultBitOffset = 0; for (auto bitRange : microcode.bitLayout) { if (bitRange.bitCount > 32) { std::println(stderr, "unexpected field size {}", bitRange.bitCount); std::abort(); } if (resultBitOffset) { std::println(" result |= ({}) << {};", emitArrayRead(typeSize, "raw", 32, bitRange), resultBitOffset); } else { std::println(" result |= {};", emitArrayRead(typeSize, "raw", 32, bitRange)); } resultBitOffset += bitRange.bitCount; } std::println(" return result;"); } } std::println(" }}"); } for (auto &condition : encoding.encodingConditions) { std::println(""); std::println(" [[nodiscard]] bool cond_{}() const {{", condition.name); std::println(" return {};", emitExpression(condition.expression)); std::println(" }}"); } if (encoding.encodingConditions.empty()) { std::println(""); std::println(" [[nodiscard]] bool cond_default() const {{"); std::println(" return true;"); std::println(" }}"); } std::println("}};"); } for (auto &enc : isa.encodings) { auto name = enc.name; if (name.starts_with("ENC_")) { name = name.substr(4); } std::println("static bool decode{}(Instruction &inst, " "std::span &words) {{", name); std::println(" if (!{}::test(words[0])) return false;", enc.name); std::println(" if (words.size() < sizeof({}) / sizeof(std::uint32_t)) " "return false;", enc.name); std::println(" {} enc;", enc.name); std::println(" std::memcpy(enc.raw, words.data(), sizeof(enc.raw));", enc.name); MicrocodeField *opcodeField = nullptr; bool hasOpcodeExtension = false; for (auto µcode : enc.microcodeFormat) { if (microcode.name == "OP") { opcodeField = µcode; continue; } if (microcode.name == "OPM") { hasOpcodeExtension = true; } } std::string_view group = enc.name; if (group.starts_with("ENC_")) { group.remove_prefix(4); } else { group = isa.encodingSubgroups.at(std::string(group)); } auto paramsIt = encodingParams.find(std::string(group)); if (opcodeField != nullptr) { if (hasOpcodeExtension) { std::println( " switch (enc.getOP() | ((enc.isOPM() ? 1 : 0) << {})) {{", getLayoutBitCount(opcodeField->bitLayout)); } else { std::println(" switch (enc.getOP()) {{"); } } for (auto &inst : isa.instructions) { bool isFirstCond = true; for (auto &instEnc : inst.encodings) { if (instEnc.name != enc.name) { continue; } if (opcodeField != nullptr && isFirstCond) { std::println(" case {}: ", instEnc.opcode); } std::println(" {}if (enc.cond_{}()) {{", isFirstCond ? "" : "else ", instEnc.condition); isFirstCond = false; std::print(" inst = Instruction::Create{}({}", group, instEnc.opcode); if (paramsIt != encodingParams.end()) { for (auto ¶mField : paramsIt->second.fields) { auto microcodeHasField = std::ranges::contains( enc.microcodeFormat, name, [](const MicrocodeField &field) { return std::string_view(field.name); }); if (paramField.typeSize == 1) { if (microcodeHasField) { std::print(", enc.is{}()", name); } else { std::print(", false"); } } else { if (microcodeHasField) { std::print(", enc.get{}()", name); } else { std::print(", 0"); } } } } std::print("); "); printComment(inst.name, 0); for (auto &op : instEnc.operands) { auto dataFormatIt = std::ranges::find( isa.dataFormats, std::string_view(op.dataFormatName), [](const DataFormat &format) { return std::string_view(format.name); }); if (dataFormatIt == isa.dataFormats.end()) { std::println( "instruction {} references to undefined data format {}", inst.name, op.dataFormatName); std::abort(); } auto typeIt = std::ranges::find(isa.operandTypes, std::string_view(op.type), [](const OperandType &type) { return std::string_view(type.name); }); if (typeIt == isa.operandTypes.end()) { std::println("instruction {} references to undefined type {}", inst.name, op.type); std::abort(); } auto operandTypeName = op.type.substr(4); std::string_view access = "OperandAccess::None"; if (op.isInput && op.isOutput) { access = "OperandAccess::ReadWrite"; } else if (op.isInput) { access = "OperandAccess::Read"; } else if (op.isOutput) { access = "OperandAccess::Write"; } if (typeIt->isNotSideEffect) { if (typeIt->microcodeFormat.empty()) { if (!op.fieldName.empty()) { std::println(" " "inst.addOperand(Operand::Create{}({}, " "DataFormat::{}, {}, " "enc.get{}()));", operandTypeName, access, op.dataFormatName.substr(4), op.size, op.fieldName); } else { std::println(" " "inst.addOperand(Operand::Create{}({}, " "DataFormat::{}, {}, " "0));", operandTypeName, access, op.dataFormatName.substr(4), op.size); } } else { std::println( " " "inst.addOperand(Operand::Create{}({}, enc.get{}()));", operandTypeName, access, op.fieldName); } } else { std::println(" inst.addOperand(Operand::Create{}({}));", operandTypeName, access); } } std::println(" }}"); } if (!isFirstCond) { std::println(" else {{"); std::println(" return false;"); std::println(" }}"); if (opcodeField != nullptr) { std::println(" break;"); } } } if (opcodeField != nullptr) { std::println(" default:"); std::println(" return false;"); std::println(" }}"); } std::println( " words = words.subspan(sizeof({}) / sizeof(std::uint32_t));", enc.name); std::println(" return true;"); std::println("}}"); } std::println(""); std::println( "void amdgpu::{}::Instruction::print(std::ostream &os) const {{", isa.archName); std::println("}}"); std::println(""); std::println("void amdgpu::{}::Operand::dump() const {{", isa.archName); std::println(" print(std::cerr);"); std::println(" std::cerr << '\\n';"); std::println("}}"); std::println(""); std::println("void amdgpu::{}::Operand::print(std::ostream &os) const {{", isa.archName); std::println("}}"); std::println(""); std::println("void amdgpu::{}::Instruction::dump() const {{", isa.archName); std::println(" print(std::cerr);"); std::println(" std::cerr << '\\n';"); std::println("}}"); std::println(""); std::println("bool amdgpu::{}::decode(Instruction &inst, " "std::span &words) {{", isa.archName); for (auto &enc : isa.encodings) { auto name = enc.name; if (name.starts_with("ENC_")) { name = name.substr(4); } std::println(" if (decode{}(inst, words)) return true;", name); } std::println(" return false;"); std::println("}}"); return 0; } if (type == OutputType::Glsl) { nlohmann::json semanticDescriptions; if (argc > 3) { std::ifstream(argv[3]) >> semanticDescriptions; } std::println("#version 460"); std::println( "#extension GL_EXT_shader_explicit_arithmetic_types : require"); for (auto &[funcGroupName, funcGroupDescription] : isa.functionalGroups) { std::map> subgroupToInstruction; for (auto &inst : isa.instructions) { if (inst.functionalGroup != funcGroupName) { continue; } if (!inst.IsConditionalBranch && inst.isBranch) { continue; } if (inst.name == "EXP") { continue; } subgroupToInstruction[inst.functionalSubgroup].push_back(&inst); } if (subgroupToInstruction.empty()) { continue; } std::println(""); std::println( "// ========================================================="); printComment(funcGroupName, 0); printComment(funcGroupDescription, 0); std::println( "// ========================================================="); for (auto &[subgroup, instList] : subgroupToInstruction) { if (!subgroup.empty()) { std::println(""); std::println( "// ---------------------------------------------------------"); printComment(subgroup, 0); std::println( "// ---------------------------------------------------------"); } for (auto inst : instList) { std::println(""); printComment(inst->description, 0); if (inst->IsConditionalBranch) { std::print("bool "); } else { std::print("void "); } std::print("{}(", toLower(inst->name)); auto instEncIt = std::ranges::find_if( inst->encodings, [&](const InstructionEncoding &enc) { auto group = enc.name; if (group.starts_with("ENC_")) { group = group.substr(4); } else { group = isa.encodingSubgroups.at(enc.name); } return encodingParams.contains(std::string(group)); }); auto &instEnc = instEncIt == inst->encodings.end() ? inst->encodings.front() : *instEncIt; auto group = instEnc.name; if (group.starts_with("ENC_")) { group = group.substr(4); } else { group = isa.encodingSubgroups.at(instEnc.name); } auto paramsIt = encodingParams.find(std::string(group)); for (bool first = true; auto &op : instEnc.operands) { if (first) { first = false; } else { std::print(", "); } auto dataFormatIt = std::ranges::find( isa.dataFormats, std::string_view(op.dataFormatName), [](const DataFormat &format) { return std::string_view(format.name); }); if (dataFormatIt == isa.dataFormats.end()) { std::println( stderr, "instruction {} references to undefined data format {}", inst->name, op.dataFormatName); std::abort(); } auto typeIt = std::ranges::find(isa.operandTypes, std::string_view(op.type), [](const OperandType &type) { return std::string_view(type.name); }); if (typeIt == isa.operandTypes.end()) { std::println(stderr, "instruction {} references to undefined type {}", inst->name, op.type); std::abort(); } if (op.isInput && op.isOutput) { std::print("inout "); } else if (op.isInput) { std::print("in "); } else if (op.isOutput) { std::print("out "); } std::uint32_t arrayCount = 1; auto selectElementType = [&](std::uint32_t componentSize, std::uint32_t componentCount, bool isFloat, bool isSigned) -> std::string { if (componentCount > 4) { std::println(stderr, "unexpected component count {}", componentCount); std::abort(); } if (componentSize > 64) { std::println(stderr, "unexpected component size {}", componentSize); std::abort(); } if (componentCount == 1) { if (componentSize == 1) { return "bool"; } if (isFloat) { return std::format("float{}_t", componentSize); } if (componentSize > 32) { return std::format("{}int64_t", isSigned ? "" : "u"); } if (componentSize > 16) { return std::format("{}int32_t", isSigned ? "" : "u"); } if (componentSize > 8) { return std::format("{}int16_t", isSigned ? "" : "u"); } return std::format("{}int8_t", isSigned ? "" : "u"); } if (componentSize == 1) { return std::format("bvec{}", componentCount); } if (isFloat) { return std::format("f{}vec{}", componentSize, componentCount); } if (componentSize > 32) { return std::format("{}64vec{}", isSigned ? "i" : "u", componentCount); } if (componentSize > 16) { return std::format("{}32vec{}", isSigned ? "i" : "u", componentCount); } if (componentSize > 8) { return std::format("{}16vec{}", isSigned ? "i" : "u", componentCount); } return std::format("{}8vec{}", isSigned ? "i" : "u", componentCount); }; auto selectTypeForBits = [&](std::uint32_t operandSize, DataFormat *dataFormat) -> std::string { if (dataFormat && dataFormat->dataType != DataType::Descriptor) { bool isFloat = dataFormat->dataType == DataType::Float; bool isSigned = dataFormat->dataType == DataType::Integer && dataFormat->fields[0][0].signedness == Signedness::Signed; if (operandSize < dataFormat->bitCount) { std::println( stderr, "unexpected operand size {}, data format size {}", operandSize, dataFormat->bitCount); std::abort(); } auto elementCount = operandSize / dataFormat->bitCount; auto componentCount = dataFormat->componentCount; auto componentSize = dataFormat->bitCount / dataFormat->componentCount; while (componentSize > 64) { componentSize /= 2; componentCount *= 2; } if (componentCount == 1 && elementCount <= 4) { componentCount = elementCount; } else if (componentCount > 4 && dataFormat->bitCount <= 64) { componentSize = dataFormat->bitCount; componentCount = 1; } else if (componentCount > 4) { arrayCount = componentCount * elementCount; componentCount = 1; } else { arrayCount = elementCount; } return selectElementType(componentSize, componentCount, isFloat, isSigned); } if (operandSize <= 64) { return selectElementType(operandSize, 1, false, false); } if (operandSize % 32) { std::println(stderr, "unexpected operand size {}", operandSize); std::abort(); } auto elementCount = operandSize / 32; if (elementCount <= 4) { return std::format("u32vec{}", elementCount); } arrayCount = elementCount; return "uint32_t"; }; if (op.dataFormatName == "FMT_ANY") { std::print("{}", selectTypeForBits(op.size, nullptr)); } else { std::print("{}", selectTypeForBits(op.size, &*dataFormatIt)); } if (!op.fieldName.empty()) { std::print(" {}", toLower(op.fieldName)); } else { if (op.type == "OPR_SSRC_SPECIAL_SCC") { std::print(" scc"); } else { std::print(" {}", toLower(op.type.substr(4))); } } if (arrayCount != 1) { std::print("[{}]", arrayCount); } } if (paramsIt != encodingParams.end()) { if (!instEnc.operands.empty()) { std::print(", "); } std::print("in {} {}", paramsIt->second.typeSize == 1 ? "bool" : std::format("uint{}_t", paramsIt->second.typeSize), paramsIt->second.fields.size() == 1 ? toLower(paramsIt->second.fields[0].name) : "params"); } std::println(") {{"); if (semanticDescriptions.contains(inst->name)) { printComment(semanticDescriptions[inst->name].get(), 1); } if (paramsIt != encodingParams.end() && paramsIt->second.fields.size() != 1) { for (auto &field : paramsIt->second.fields) { std::println( " {} {} = {};", field.typeSize == 1 ? "bool" : std::format("uint{}_t", field.typeSize), toLower(field.name), emitVariableRead(field.typeSize, "params", 32, { .bitCount = field.bitCount, .bitOffset = field.bitOffset, }, true)); } } if (inst->IsConditionalBranch) { std::println(" return false;"); } std::println("}}"); } } } return 0; } return 1; }