From 8a4bb6e3b0bffa0ed79b51347b12ee4293b622a7 Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Sat, 22 Aug 2026 18:49:08 +0800 Subject: [CodeGen] Generate RISC-V macro fusion rules --- .../utils/TableGen/Common/CodeGenMacroFusion.cpp | 175 +++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 backend/utils/TableGen/Common/CodeGenMacroFusion.cpp (limited to 'backend/utils/TableGen/Common/CodeGenMacroFusion.cpp') diff --git a/backend/utils/TableGen/Common/CodeGenMacroFusion.cpp b/backend/utils/TableGen/Common/CodeGenMacroFusion.cpp new file mode 100644 index 0000000..7c4ea1c --- /dev/null +++ b/backend/utils/TableGen/Common/CodeGenMacroFusion.cpp @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "CodeGenTarget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc::targetgen { +namespace { + +[[nodiscard]] const NormalizedInstruction *findInstruction(const NormalizedTargetModel &Model, + std::string_view Symbol) noexcept { + const auto Position = + std::ranges::find(Model.Instructions, Symbol, &NormalizedInstruction::Symbol); + return Position == Model.Instructions.end() ? nullptr : &*Position; +} + +[[nodiscard]] bool isOperandKind(const NormalizedInstruction &Instruction, + std::uint32_t OperandIndex, std::string_view Kind) noexcept { + return OperandIndex < Instruction.Operands.size() && + std::ranges::contains(Instruction.Operands[OperandIndex].Kinds, Kind); +} + +[[nodiscard]] std::optional +normalizeInstructionSide(std::string_view Side) noexcept { + if (Side == "first") + return NormalizedMacroFusionInstruction::First; + if (Side == "second") + return NormalizedMacroFusionInstruction::Second; + return std::nullopt; +} + +} // namespace + +bool CodeGenTarget::normalizeMacroFusionRules(NormalizedTargetModel &Result) { + std::unordered_set Symbols; + std::unordered_set Features; + for (const SourceMacroFusionRule &Rule : Input.MacroFusionRules) { + if (!isIdentifier(Rule.Symbol) || Rule.Feature.empty() || Rule.FirstOpcodeSymbols.empty() || + Rule.SecondOpcodeSymbols.empty() || !Symbols.insert(Rule.Symbol).second || + !Features.insert(Rule.Feature).second) { + fail(Rule.Range, + "macro fusion rules require unique identifiers and features plus nonempty opcode sets"); + return false; + } + + NormalizedMacroFusionRule Normalized{ + .Symbol = Rule.Symbol, + .Feature = Rule.Feature, + .FirstOpcodes = {}, + .SecondOpcodes = {}, + .OpcodePairs = {}, + .ImmediatePredicates = {}, + .RegisterInequalities = {}, + .ImmediateRelations = {}, + .FirstDefinitionOperand = Rule.FirstDefinitionOperand, + .SecondUseOperand = Rule.SecondUseOperand, + .SecondDefinitionOperand = Rule.SecondDefinitionOperand, + .CommutableSecondUseOperand = Rule.CommutableSecondUseOperand, + .RequireSingleNonDebugUse = Rule.RequireSingleNonDebugUse, + .RequireSecondDestinationTie = Rule.RequireSecondDestinationTie, + }; + std::unordered_set FirstOpcodes; + std::unordered_set SecondOpcodes; + std::vector FirstInstructions; + std::vector SecondInstructions; + const auto NormalizeOpcodes = + [this, &Result](const std::vector &OpcodeSymbols, + std::unordered_set &Opcodes, + std::vector &Output, + std::vector &Instructions, TextRange Range) { + for (const std::string &Symbol : OpcodeSymbols) { + const NormalizedInstruction *Instruction = findInstruction(Result, Symbol); + if (Instruction == nullptr || !Opcodes.insert(Instruction->ID).second) { + fail(Range, "macro fusion opcodes must name distinct declared target instructions"); + return false; + } + Output.push_back(Instruction->ID); + Instructions.push_back(Instruction); + } + return true; + }; + if (!NormalizeOpcodes(Rule.FirstOpcodeSymbols, FirstOpcodes, Normalized.FirstOpcodes, + FirstInstructions, Rule.Range) || + !NormalizeOpcodes(Rule.SecondOpcodeSymbols, SecondOpcodes, Normalized.SecondOpcodes, + SecondInstructions, Rule.Range)) { + return false; + } + + const auto AllHaveOperandKind = [](const auto &Instructions, std::uint32_t Operand, + std::string_view Kind) { + return std::ranges::all_of(Instructions, [Operand, Kind](const auto *Instruction) { + return isOperandKind(*Instruction, Operand, Kind); + }); + }; + if (!AllHaveOperandKind(FirstInstructions, Rule.FirstDefinitionOperand, "Register") || + !AllHaveOperandKind(SecondInstructions, Rule.SecondUseOperand, "Register") || + (Rule.RequireSecondDestinationTie && + !AllHaveOperandKind(SecondInstructions, Rule.SecondDefinitionOperand, "Register")) || + (Rule.CommutableSecondUseOperand && + !AllHaveOperandKind(SecondInstructions, *Rule.CommutableSecondUseOperand, "Register"))) { + fail(Rule.Range, "macro fusion linkage must reference register operands of every opcode"); + return false; + } + + std::unordered_set OpcodePairs; + for (const SourceMacroFusionOpcodePair &Pair : Rule.OpcodePairs) { + const NormalizedInstruction *First = findInstruction(Result, Pair.FirstOpcodeSymbol); + const NormalizedInstruction *Second = findInstruction(Result, Pair.SecondOpcodeSymbol); + if (First == nullptr || Second == nullptr || !FirstOpcodes.contains(First->ID) || + !SecondOpcodes.contains(Second->ID)) { + fail(Pair.Range, "macro fusion opcode pair must belong to the rule opcode sets"); + return false; + } + const std::uint64_t Key = (static_cast(First->ID) << 32U) | Second->ID; + if (!OpcodePairs.insert(Key).second) { + fail(Pair.Range, "macro fusion opcode pairs must be distinct"); + return false; + } + Normalized.OpcodePairs.push_back({First->ID, Second->ID}); + } + + for (const SourceMacroFusionImmediatePredicate &Predicate : Rule.ImmediatePredicates) { + const auto Side = normalizeInstructionSide(Predicate.Instruction); + const auto &Instructions = + Side == NormalizedMacroFusionInstruction::First ? FirstInstructions : SecondInstructions; + if (!Side || Predicate.Minimum > Predicate.Maximum || + !AllHaveOperandKind(Instructions, Predicate.OperandIndex, "Immediate")) { + fail( + Predicate.Range, + "macro fusion immediate predicate requires a valid side, range, and immediate operand"); + return false; + } + Normalized.ImmediatePredicates.push_back( + {*Side, Predicate.OperandIndex, Predicate.Minimum, Predicate.Maximum}); + } + + for (const SourceMacroFusionRegisterInequality &Predicate : Rule.RegisterInequalities) { + const auto Side = normalizeInstructionSide(Predicate.Instruction); + const auto &Instructions = + Side == NormalizedMacroFusionInstruction::First ? FirstInstructions : SecondInstructions; + if (!Side || Predicate.LeftOperand == Predicate.RightOperand || + !AllHaveOperandKind(Instructions, Predicate.LeftOperand, "Register") || + !AllHaveOperandKind(Instructions, Predicate.RightOperand, "Register")) { + fail( + Predicate.Range, + "macro fusion register inequality requires distinct register operands on a valid side"); + return false; + } + Normalized.RegisterInequalities.push_back( + {*Side, Predicate.LeftOperand, Predicate.RightOperand}); + } + + for (const SourceMacroFusionImmediateRelation &Relation : Rule.ImmediateRelations) { + if (Relation.Kind != "less_equal" || + !AllHaveOperandKind(FirstInstructions, Relation.FirstOperand, "Immediate") || + !AllHaveOperandKind(SecondInstructions, Relation.SecondOperand, "Immediate")) { + fail(Relation.Range, + "macro fusion immediate relation must be less_equal over immediate operands"); + return false; + } + Normalized.ImmediateRelations.push_back({Relation.FirstOperand, Relation.SecondOperand}); + } + Result.MacroFusionRules.push_back(std::move(Normalized)); + } + return true; +} + +} // namespace rcc::targetgen -- cgit v1.2.3