diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-16 17:28:49 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-16 17:28:49 +0800 |
| commit | 864a29abbdce86353ec91c3587757711d3a42f10 (patch) | |
| tree | 772390cad70fe8e47044f5c921174b254bd8297a /backend/lib | |
| parent | ca25d25d4c5131491a50b1a94e63e9b2d3c34ed4 (diff) | |
| download | rcc-project-864a29abbdce86353ec91c3587757711d3a42f10.tar.gz rcc-project-864a29abbdce86353ec91c3587757711d3a42f10.tar.xz | |
[RISCV][GlobalISel] Generate integer comparisons
Diffstat (limited to 'backend/lib')
| -rw-r--r-- | backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp | 679 | ||||
| -rw-r--r-- | backend/lib/CodeGen/GlobalISel/InstructionSelect.cpp | 10 | ||||
| -rw-r--r-- | backend/lib/CodeGen/TargetDescription.cpp | 13 | ||||
| -rw-r--r-- | backend/lib/TableGen/Parser.cpp | 173 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp | 176 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVGISel.rccdesc | 652 |
6 files changed, 1350 insertions, 353 deletions
diff --git a/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp b/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp index 38fb66d..7f7cb63 100644 --- a/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp +++ b/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp @@ -6,16 +6,21 @@ #include "rcc/CodeGen/GlobalISel/Utils.h" #include "rcc/CodeGen/LowLevelType.h" #include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineIRBuilder.h" #include "rcc/CodeGen/MachineInstr.h" #include "rcc/CodeGen/MachineOperand.h" #include "rcc/CodeGen/MachineRegisterInfo.h" #include "rcc/CodeGen/TargetDescription.h" +#include <algorithm> #include <charconv> #include <cstddef> #include <cstdint> +#include <limits> #include <optional> +#include <ranges> #include <string> +#include <utility> #include <vector> namespace rcc { @@ -100,30 +105,58 @@ namespace { return Integer != nullptr && static_cast<std::uint32_t>(*Integer) == Step.Value; } -[[nodiscard]] Expected<bool> matchesOperandDefConstant(const MachineInstr &Instruction, - const MachineFunction &Function, - const GIMatchTableInstruction &Step) { - const MachineOperand *Operand = Instruction.operandAt(Step.OperandIndex); +[[nodiscard]] Expected<std::optional<std::int64_t>> +operandDefConstantValue(const MachineInstr &Instruction, const MachineFunction &Function, + std::size_t OperandIndex) { + const MachineOperand *Operand = Instruction.operandAt(OperandIndex); const Register *Reg = Operand == nullptr ? nullptr : Operand->getIf<Register>(); if (Reg == nullptr || !Reg->isVirtual()) - return false; + return std::optional<std::int64_t>{}; const MachineInstr *Definition = Function.registerInfo().getVRegDef(*Reg); if (Definition == nullptr || Definition->opcode() != opcode(GenericOpcode::G_CONSTANT)) - return false; + return std::optional<std::int64_t>{}; const MachineOperand *Value = Definition->operandAt(1); if (Value == nullptr) - return false; + return std::optional<std::int64_t>{}; if (const std::int64_t *Immediate = Value->getIf<std::int64_t>()) - return *Immediate == Step.Immediate; + return *Immediate; const auto *Integer = Value->getIf<IntegerImmediateOperand>(); if (Integer == nullptr) - return false; - if (Step.Immediate == 0) - return Integer->Value.isZero(); + return std::optional<std::int64_t>{}; const std::string Text = Integer->Value.toSignedDecimal(); std::int64_t Parsed = 0; const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Parsed); - return Error == std::errc{} && End == Text.data() + Text.size() && Parsed == Step.Immediate; + if (Error != std::errc{} || End != Text.data() + Text.size()) + return std::optional<std::int64_t>{}; + return Parsed; +} + +[[nodiscard]] Expected<bool> +matchesOperandDefConstantRanges(const GIMatchTable &Table, const MachineInstr &Instruction, + const MachineFunction &Function, + const GIMatchTableInstruction &Step) { + const std::size_t First = Step.Value; + const std::size_t Count = Step.Auxiliary; + if (Count == 0 || First > Table.ConstantRanges.size() || + Count > Table.ConstantRanges.size() - First) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table has an invalid constant range"); + } + auto Value = operandDefConstantValue(Instruction, Function, Step.OperandIndex); + if (!Value) + return std::unexpected(Value.error()); + if (!*Value) + return false; + for (std::size_t Index = First; Index < First + Count; ++Index) { + const GIMatchTableConstantRange &Range = Table.ConstantRanges[Index]; + if (Range.Minimum > Range.Maximum) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table has an inverted constant range"); + } + if (**Value >= Range.Minimum && **Value <= Range.Maximum) + return true; + } + return false; } [[nodiscard]] Expected<RegisterOperandFlags> registerFlagsFromRaw(std::uint32_t Raw) { @@ -143,23 +176,45 @@ namespace { } struct MatchedRule final { + struct Temporary final { + std::uint32_t ID = 0; + std::uint32_t TypeFromOperand = 0; + }; + struct RenderedOperand final { enum class Kind : std::uint8_t { RootOperand, PhysicalRegister, + Temporary, + CapturedImmediate, + Immediate, }; Kind SourceKind = Kind::RootOperand; std::uint32_t Value = 0; std::uint32_t Flags = 0; + GIMatchImmediateTransform Transform = GIMatchImmediateTransform::Identity; + std::int64_t Immediate = 0; + }; + + struct Instruction final { + Opcode ResultOpcode; + bool ReplacesRoot = false; + std::vector<RenderedOperand> Operands; }; - Opcode ResultOpcode; - std::optional<std::vector<RenderedOperand>> OperandLayout; - std::vector<std::int64_t> AppendedImmediates; + std::vector<Temporary> Temporaries; + std::vector<Instruction> Instructions; std::vector<Register> EraseOperandDefinitions; }; +[[nodiscard]] bool hasTemporary(const MatchedRule &Rule, std::uint32_t ID) { + return std::ranges::any_of(Rule.Temporaries, + [ID](const MatchedRule::Temporary &Temporary) { + return Temporary.ID == ID; + }); +} + [[nodiscard]] Expected<std::optional<MatchedRule>> matchRule(const GIMatchTable &Table, const GIMatchTableRule &Rule, const MachineInstr &Instruction, @@ -172,23 +227,24 @@ struct MatchedRule final { } MatchedRule Result; - bool HasResultOpcode = false; bool Applying = false; bool Complete = false; for (std::size_t Index = First; Index < First + Count; ++Index) { const GIMatchTableInstruction &Step = Table.Instructions[Index]; switch (Step.Opcode) { case GIMatchTableOpcode::CheckOpcode: - if (Applying) + if (Applying) { return makeUnexpected(ErrorCode::VerificationError, - "generated match table performs a predicate after rendering"); + "generated match table performs a predicate after an action"); + } if (Instruction.opcode().value() != Step.Value) return std::optional<MatchedRule>{}; break; case GIMatchTableOpcode::CheckOperandType: { - if (Applying) + if (Applying) { return makeUnexpected(ErrorCode::VerificationError, - "generated match table performs a predicate after rendering"); + "generated match table performs a predicate after an action"); + } auto Matched = matchesType(Instruction, Function, Step); if (!Matched) return std::unexpected(Matched.error()); @@ -197,9 +253,10 @@ struct MatchedRule final { break; } case GIMatchTableOpcode::CheckOperandBank: { - if (Applying) + if (Applying) { return makeUnexpected(ErrorCode::VerificationError, - "generated match table performs a predicate after rendering"); + "generated match table performs a predicate after an action"); + } auto Matched = matchesBank(Instruction, Function, Step); if (!Matched) return std::unexpected(Matched.error()); @@ -208,9 +265,10 @@ struct MatchedRule final { break; } case GIMatchTableOpcode::CheckOperandClass: { - if (Applying) + if (Applying) { return makeUnexpected(ErrorCode::VerificationError, - "generated match table performs a predicate after rendering"); + "generated match table performs a predicate after an action"); + } auto Matched = matchesClass(Instruction, Function, Step); if (!Matched) return std::unexpected(Matched.error()); @@ -219,9 +277,10 @@ struct MatchedRule final { break; } case GIMatchTableOpcode::CheckIntegerPredicate: { - if (Applying) + if (Applying) { return makeUnexpected(ErrorCode::VerificationError, - "generated match table performs a predicate after rendering"); + "generated match table performs a predicate after an action"); + } auto Matched = matchesIntegerPredicate(Instruction, Step); if (!Matched) return std::unexpected(Matched.error()); @@ -229,84 +288,153 @@ struct MatchedRule final { return std::optional<MatchedRule>{}; break; } - case GIMatchTableOpcode::CheckOperandDefConstant: { - if (Applying) + case GIMatchTableOpcode::CheckOperandDefConstantRanges: { + if (Applying) { return makeUnexpected(ErrorCode::VerificationError, - "generated match table performs a predicate after rendering"); - auto Matched = matchesOperandDefConstant(Instruction, Function, Step); + "generated match table performs a predicate after an action"); + } + auto Matched = matchesOperandDefConstantRanges(Table, Instruction, Function, Step); if (!Matched) return std::unexpected(Matched.error()); if (!*Matched) return std::optional<MatchedRule>{}; break; } + case GIMatchTableOpcode::CreateTemporary: { + if (Complete || Step.Value == 0 || hasTemporary(Result, Step.Value)) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table has an invalid temporary"); + } + auto Type = operandType(Instruction, Function, Step.OperandIndex); + if (!Type) + return std::unexpected(Type.error()); + if (!*Type) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table temporary has no type source"); + } + Applying = true; + Result.Temporaries.push_back({ + .ID = Step.Value, + .TypeFromOperand = Step.OperandIndex, + }); + break; + } case GIMatchTableOpcode::MutateOpcode: - if (Complete || HasResultOpcode || Step.Value == 0) + if (Complete || Step.Value == 0 || !Result.Instructions.empty()) { return makeUnexpected(ErrorCode::VerificationError, - "generated match table has an invalid opcode mutation"); + "generated match table has an invalid root mutation"); + } Applying = true; - Result.ResultOpcode = Opcode::fromValue(Step.Value); - HasResultOpcode = true; + Result.Instructions.push_back({ + .ResultOpcode = Opcode::fromValue(Step.Value), + .ReplacesRoot = true, + .Operands = {}, + }); + break; + case GIMatchTableOpcode::BuildInstruction: + if (Complete || Step.Value == 0 || Result.Instructions.empty()) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table has an invalid instruction action"); + } + Applying = true; + Result.Instructions.push_back({ + .ResultOpcode = Opcode::fromValue(Step.Value), + .ReplacesRoot = false, + .Operands = {}, + }); break; case GIMatchTableOpcode::RenderOperand: - if (Complete || !HasResultOpcode) + if (Complete || Result.Instructions.empty() || + Instruction.operandAt(Step.OperandIndex) == nullptr || + Instruction.operandAt(Step.OperandIndex)->isImplicit()) { return makeUnexpected(ErrorCode::VerificationError, "generated match table has an invalid operand renderer"); - Applying = true; - if (!Result.OperandLayout) - Result.OperandLayout.emplace(); - Result.OperandLayout->push_back({ + } + Result.Instructions.back().Operands.push_back({ .SourceKind = MatchedRule::RenderedOperand::Kind::RootOperand, .Value = Step.OperandIndex, - .Flags = 0, }); break; case GIMatchTableOpcode::RenderPhysicalRegister: - if (Complete || !HasResultOpcode || Step.Value == 0) + if (Complete || Result.Instructions.empty() || Step.Value == 0) { return makeUnexpected(ErrorCode::VerificationError, "generated match table has an invalid physical register renderer"); - Applying = true; - if (!Result.OperandLayout) - Result.OperandLayout.emplace(); - Result.OperandLayout->push_back({ + } + Result.Instructions.back().Operands.push_back({ .SourceKind = MatchedRule::RenderedOperand::Kind::PhysicalRegister, .Value = Step.Value, .Flags = Step.Auxiliary, }); break; - case GIMatchTableOpcode::AppendImmediate: - if (Complete || Result.OperandLayout) + case GIMatchTableOpcode::RenderTemporary: + if (Complete || Result.Instructions.empty() || !hasTemporary(Result, Step.Value)) { return makeUnexpected(ErrorCode::VerificationError, - "generated match table renders after completion"); - Applying = true; - Result.AppendedImmediates.push_back(Step.Immediate); + "generated match table has an invalid temporary renderer"); + } + Result.Instructions.back().Operands.push_back({ + .SourceKind = MatchedRule::RenderedOperand::Kind::Temporary, + .Value = Step.Value, + .Flags = Step.Auxiliary, + }); + break; + case GIMatchTableOpcode::RenderCapturedImmediate: { + if (Complete || Result.Instructions.empty() || + Step.Value > static_cast<std::uint32_t>(GIMatchImmediateTransform::PlusOne)) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table has an invalid captured immediate renderer"); + } + auto Value = operandDefConstantValue(Instruction, Function, Step.OperandIndex); + if (!Value) + return std::unexpected(Value.error()); + if (!*Value) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table lost a captured constant"); + } + Result.Instructions.back().Operands.push_back({ + .SourceKind = MatchedRule::RenderedOperand::Kind::CapturedImmediate, + .Value = Step.OperandIndex, + .Transform = static_cast<GIMatchImmediateTransform>(Step.Value), + }); + break; + } + case GIMatchTableOpcode::RenderImmediate: + if (Complete || Result.Instructions.empty()) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table has an invalid immediate renderer"); + } + Result.Instructions.back().Operands.push_back({ + .SourceKind = MatchedRule::RenderedOperand::Kind::Immediate, + .Immediate = Step.Immediate, + }); break; case GIMatchTableOpcode::EraseOperandDefinition: { - if (Complete || !HasResultOpcode) + if (Complete || Result.Instructions.empty()) { return makeUnexpected(ErrorCode::VerificationError, "generated match table has an invalid erased definition"); + } const MachineOperand *Operand = Instruction.operandAt(Step.OperandIndex); const Register *Reg = Operand == nullptr ? nullptr : Operand->getIf<Register>(); if (Reg == nullptr || !Reg->isVirtual()) { return makeUnexpected(ErrorCode::VerificationError, "generated match table erases a non-virtual operand definition"); } - Applying = true; Result.EraseOperandDefinitions.push_back(*Reg); break; } case GIMatchTableOpcode::Complete: - if (Complete || !HasResultOpcode || Index + 1 != First + Count) + if (Complete || Result.Instructions.empty() || Index + 1 != First + Count) { return makeUnexpected(ErrorCode::VerificationError, "generated match table has an invalid completion action"); + } Complete = true; break; } } - if (!Complete || !HasResultOpcode) + if (!Complete || Result.Instructions.empty() || !Result.Instructions.front().ReplacesRoot) { return makeUnexpected(ErrorCode::VerificationError, "generated match table rule does not complete rendering"); + } return std::optional<MatchedRule>(std::move(Result)); } @@ -342,16 +470,210 @@ restoreInstruction(MachineInstr &Instruction, Opcode OriginalOpcode, return {}; } -[[nodiscard]] Expected<void> applyRule(const MatchedRule &Rule, MachineInstr &Instruction, - MachineFunction &Function, const TargetDescription &Target) { - const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Rule.ResultOpcode); - if (Descriptor == nullptr) +struct MaterializedTemporary final { + std::uint32_t ID = 0; + Register Reg; +}; + +struct RegisterConstraintSnapshot final { + Register Reg; + std::optional<RegisterBankID> Bank; + std::optional<RegisterClassID> RegClass; +}; + +[[nodiscard]] const MaterializedTemporary * +findTemporary(const std::vector<MaterializedTemporary> &Temporaries, std::uint32_t ID) { + const auto Position = + std::ranges::find(Temporaries, ID, &MaterializedTemporary::ID); + return Position == Temporaries.end() ? nullptr : &*Position; +} + +[[nodiscard]] Expected<void> +eraseTemporaries(MachineRegisterInfo &Registers, + const std::vector<MaterializedTemporary> &Temporaries) { + for (auto Iterator = Temporaries.rbegin(); Iterator != Temporaries.rend(); ++Iterator) { + if (!Registers.isKnownVirtualRegister(Iterator->Reg)) + continue; + auto Erased = Registers.eraseVirtualRegister(Iterator->Reg); + if (!Erased) + return std::unexpected(Erased.error()); + } + return {}; +} + +[[nodiscard]] Expected<void> +restoreRegisterConstraints(MachineRegisterInfo &Registers, + const std::vector<RegisterConstraintSnapshot> &Snapshots) { + for (auto Iterator = Snapshots.rbegin(); Iterator != Snapshots.rend(); ++Iterator) { + if (!Registers.isKnownVirtualRegister(Iterator->Reg)) + continue; + if (Iterator->Bank) { + auto Restored = Registers.setRegisterBank(Iterator->Reg, *Iterator->Bank); + if (!Restored) + return std::unexpected(Restored.error()); + } else { + auto Cleared = Registers.clearRegisterBank(Iterator->Reg); + if (!Cleared) + return std::unexpected(Cleared.error()); + } + if (Iterator->RegClass) { + auto Restored = Registers.setRegisterClass(Iterator->Reg, *Iterator->RegClass); + if (!Restored) + return std::unexpected(Restored.error()); + } else { + auto Cleared = Registers.clearRegisterClass(Iterator->Reg); + if (!Cleared) + return std::unexpected(Cleared.error()); + } + } + return {}; +} + +[[nodiscard]] Expected<void> +constrainRenderedInstruction(const InstructionDescriptor &Descriptor, + std::span<const MachineOperand> Operands, + MachineFunction &Function, const TargetDescription &Target, + std::vector<RegisterConstraintSnapshot> &Snapshots) { + if (Descriptor.Operands.empty() && !Operands.empty()) { return makeUnexpected(ErrorCode::VerificationError, - "generated match table selects an unknown instruction opcode"); + "generated match table result has no operand descriptors"); + } + MachineRegisterInfo &Registers = Function.registerInfo(); + const auto Remember = [&Snapshots](Register Reg, const VirtualRegisterInfo &Info) { + if (std::ranges::find(Snapshots, Reg, &RegisterConstraintSnapshot::Reg) == Snapshots.end()) + Snapshots.push_back({.Reg = Reg, .Bank = Info.Bank, .RegClass = Info.RegClass}); + }; + + for (std::size_t Index = 0; Index < Operands.size(); ++Index) { + const Register *Reg = Operands[Index].getIf<Register>(); + if (Reg == nullptr || !Reg->isVirtual()) + continue; + + const OperandDescriptor &OperandDescriptor = + Index < Descriptor.Operands.size() ? Descriptor.Operands[Index] : Descriptor.Operands.back(); + if (!OperandDescriptor.RegBank && !OperandDescriptor.RegClass) + continue; + + auto Info = Registers.virtualRegisterInfo(*Reg); + if (!Info) + return std::unexpected(Info.error()); + if (OperandDescriptor.RegBank) { + if (Info->get().Bank && *Info->get().Bank != *OperandDescriptor.RegBank) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table result has an incompatible register bank"); + } + if (!Info->get().Bank) { + Remember(*Reg, Info->get()); + auto SetBank = Registers.setRegisterBank(*Reg, *OperandDescriptor.RegBank); + if (!SetBank) + return std::unexpected(SetBank.error()); + } + } + if (OperandDescriptor.RegClass) { + if (Info->get().RegClass && + !Target.registers().isSubclass(*Info->get().RegClass, *OperandDescriptor.RegClass)) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table result has an incompatible register class"); + } + if (!Info->get().RegClass) { + Remember(*Reg, Info->get()); + auto SetClass = Registers.setRegisterClass(*Reg, *OperandDescriptor.RegClass); + if (!SetClass) + return std::unexpected(SetClass.error()); + } + } + } + return {}; +} + +[[nodiscard]] Expected<MachineOperand> +renderOperand(const MatchedRule::RenderedOperand &Rendered, const MachineInstr &Instruction, + const MachineFunction &Function, const TargetDescription &Target, + const std::vector<MaterializedTemporary> &Temporaries, + std::size_t OriginalExplicitCount) { + switch (Rendered.SourceKind) { + case MatchedRule::RenderedOperand::Kind::RootOperand: + if (Rendered.Value >= OriginalExplicitCount) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table renders an implicit or missing operand"); + } + return Instruction.operandAt(Rendered.Value)->cloneDetached(); + case MatchedRule::RenderedOperand::Kind::PhysicalRegister: { + auto Physical = PhysicalRegister::create(Rendered.Value); + if (!Physical || Target.registers().physicalRegister(*Physical) == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table renders an unknown physical register"); + } + auto Flags = registerFlagsFromRaw(Rendered.Flags); + if (!Flags) + return std::unexpected(Flags.error()); + return MachineOperand::registerOperand(Register::fromPhysical(*Physical), *Flags); + } + case MatchedRule::RenderedOperand::Kind::Temporary: { + const MaterializedTemporary *Temporary = findTemporary(Temporaries, Rendered.Value); + if (Temporary == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table renders an unknown temporary"); + } + auto Flags = registerFlagsFromRaw(Rendered.Flags); + if (!Flags) + return std::unexpected(Flags.error()); + return MachineOperand::registerOperand(Temporary->Reg, *Flags); + } + case MatchedRule::RenderedOperand::Kind::CapturedImmediate: { + auto Captured = operandDefConstantValue(Instruction, Function, Rendered.Value); + if (!Captured) + return std::unexpected(Captured.error()); + if (!*Captured) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table lost a captured constant"); + } + std::int64_t Value = **Captured; + switch (Rendered.Transform) { + case GIMatchImmediateTransform::Identity: + break; + case GIMatchImmediateTransform::Negate: + if (Value == std::numeric_limits<std::int64_t>::min()) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table immediate negation overflows"); + } + Value = -Value; + break; + case GIMatchImmediateTransform::PlusOne: + if (Value == std::numeric_limits<std::int64_t>::max()) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table immediate increment overflows"); + } + ++Value; + break; + } + return MachineOperand::immediate(Value); + } + case MatchedRule::RenderedOperand::Kind::Immediate: + return MachineOperand::immediate(Rendered.Immediate); + } + return makeUnexpected(ErrorCode::VerificationError, + "generated match table has an invalid operand renderer"); +} + +template <typename Finalizer> +[[nodiscard]] Expected<void> +applyRule(const MatchedRule &Rule, MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target, Finalizer &&Finalize) { + const InstructionDescriptor *InputDescriptor = + Target.instructionDescriptor(Instruction.opcode()); + if (InputDescriptor == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table input has no instruction descriptor"); + } + const MachineInstrFlags InputFlags = Instruction.flags(); + const bool InputMayRaiseFPException = + InputDescriptor->Flags.has(InstructionFlag::MayRaiseFPException); const std::size_t OriginalExplicitCount = explicitOperandCount(Instruction); const std::size_t OriginalOperandCount = Instruction.operandCount(); const Opcode OriginalOpcode = Instruction.opcode(); + std::vector<MachineOperand> OriginalOperands; OriginalOperands.reserve(OriginalOperandCount); for (const MachineOperand &Operand : Instruction.operands()) @@ -364,110 +686,201 @@ restoreInstruction(MachineInstr &Instruction, Opcode OriginalOpcode, Ties.emplace_back(Index, *Other); } - std::vector<MachineOperand> NewOperands; + std::vector<MaterializedTemporary> Temporaries; + Temporaries.reserve(Rule.Temporaries.size()); + for (const MatchedRule::Temporary &Temporary : Rule.Temporaries) { + const MachineOperand *SourceOperand = Instruction.operandAt(Temporary.TypeFromOperand); + const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); + if (Source == nullptr || !Source->isVirtual()) { + auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries); + if (!Cleanup) + return std::unexpected(Cleanup.error()); + return makeUnexpected(ErrorCode::VerificationError, + "generated match table temporary has an invalid type source"); + } + auto SourceInfo = Function.registerInfo().virtualRegisterInfo(*Source); + if (!SourceInfo) { + auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries); + if (!Cleanup) + return std::unexpected(Cleanup.error()); + return std::unexpected(SourceInfo.error()); + } + const std::optional<LowLevelType> Type = SourceInfo->get().Type; + if (!Type) { + auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries); + if (!Cleanup) + return std::unexpected(Cleanup.error()); + return makeUnexpected(ErrorCode::VerificationError, + "generated match table temporary type source has no type"); + } + auto Reg = Function.registerInfo().createVirtualRegister(*Type); + if (!Reg) { + auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries); + if (!Cleanup) + return std::unexpected(Cleanup.error()); + return std::unexpected(Reg.error()); + } + Temporaries.push_back({.ID = Temporary.ID, .Reg = *Reg}); + } + + std::vector<std::vector<MachineOperand>> RenderedInstructions; + RenderedInstructions.reserve(Rule.Instructions.size()); std::vector<std::optional<std::size_t>> SourceToOutput(OriginalOperandCount); - std::size_t NewExplicitCount = 0; - if (Rule.OperandLayout) { - NewOperands.reserve(Rule.OperandLayout->size() + OriginalOperandCount - OriginalExplicitCount); - for (const MatchedRule::RenderedOperand &Rendered : *Rule.OperandLayout) { - if (Rendered.SourceKind == MatchedRule::RenderedOperand::Kind::RootOperand) { - if (Rendered.Value >= OriginalExplicitCount) { - return makeUnexpected(ErrorCode::VerificationError, - "generated match table renders an implicit or missing operand"); - } + for (std::size_t InstructionIndex = 0; InstructionIndex < Rule.Instructions.size(); + ++InstructionIndex) { + const MatchedRule::Instruction &Planned = Rule.Instructions[InstructionIndex]; + const InstructionDescriptor *Descriptor = + Target.instructionDescriptor(Planned.ResultOpcode); + if (Descriptor == nullptr || + (!Descriptor->Variadic && Descriptor->Operands.size() != Planned.Operands.size())) { + auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries); + if (!Cleanup) + return std::unexpected(Cleanup.error()); + return makeUnexpected(ErrorCode::VerificationError, + "generated match table result has an incompatible descriptor"); + } + std::vector<MachineOperand> Operands; + Operands.reserve(Planned.Operands.size()); + for (const MatchedRule::RenderedOperand &Rendered : Planned.Operands) { + auto Operand = renderOperand(Rendered, Instruction, Function, Target, Temporaries, + OriginalExplicitCount); + if (!Operand) { + auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries); + if (!Cleanup) + return std::unexpected(Cleanup.error()); + return std::unexpected(Operand.error()); + } + if (InstructionIndex == 0 && + Rendered.SourceKind == MatchedRule::RenderedOperand::Kind::RootOperand) { if (SourceToOutput[Rendered.Value] && Instruction.operandAt(Rendered.Value)->tiedOperandIndex()) { + auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries); + if (!Cleanup) + return std::unexpected(Cleanup.error()); return makeUnexpected(ErrorCode::VerificationError, "generated match table duplicates a tied operand"); } - SourceToOutput[Rendered.Value] = NewOperands.size(); - NewOperands.push_back(Instruction.operandAt(Rendered.Value)->cloneDetached()); - } else { - auto Physical = PhysicalRegister::create(Rendered.Value); - if (!Physical || Target.registers().physicalRegister(*Physical) == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "generated match table renders an unknown physical register"); - auto Flags = registerFlagsFromRaw(Rendered.Flags); - if (!Flags) - return std::unexpected(Flags.error()); - auto Operand = MachineOperand::registerOperand(Register::fromPhysical(*Physical), *Flags); - if (!Operand) - return std::unexpected(Operand.error()); - NewOperands.push_back(std::move(*Operand)); + SourceToOutput[Rendered.Value] = Operands.size(); } + Operands.push_back(std::move(*Operand)); } - NewExplicitCount = NewOperands.size(); - } else { - NewOperands.reserve(OriginalOperandCount + Rule.AppendedImmediates.size()); - for (std::size_t Index = 0; Index < OriginalExplicitCount; ++Index) { - SourceToOutput[Index] = NewOperands.size(); - NewOperands.push_back(Instruction.operandAt(Index)->cloneDetached()); - } - NewExplicitCount = NewOperands.size(); - for (const std::int64_t Immediate : Rule.AppendedImmediates) - NewOperands.push_back(MachineOperand::immediate(Immediate)); - NewExplicitCount = NewOperands.size(); + RenderedInstructions.push_back(std::move(Operands)); } - for (std::size_t Index = OriginalExplicitCount; Index < OriginalOperandCount; ++Index) { - SourceToOutput[Index] = NewOperands.size(); - NewOperands.push_back(Instruction.operandAt(Index)->cloneDetached()); + + std::vector<MachineInstr *> AddedInstructions; + std::vector<RegisterConstraintSnapshot> ConstraintSnapshots; + bool RootModified = false; + const auto Rollback = [&](Error Failure) -> Expected<void> { + for (auto Iterator = AddedInstructions.rbegin(); Iterator != AddedInstructions.rend(); + ++Iterator) { + if ((*Iterator)->parent() == nullptr) + continue; + auto Erased = eraseInstr(**Iterator, Function.registerInfo(), Target); + if (!Erased) + return std::unexpected(Erased.error()); + } + if (RootModified) { + auto Restored = restoreInstruction(Instruction, OriginalOpcode, OriginalOperands, Ties); + if (!Restored) + return std::unexpected(Restored.error()); + } + auto RestoredConstraints = + restoreRegisterConstraints(Function.registerInfo(), ConstraintSnapshots); + if (!RestoredConstraints) + return std::unexpected(RestoredConstraints.error()); + auto ErasedTemporaries = eraseTemporaries(Function.registerInfo(), Temporaries); + if (!ErasedTemporaries) + return std::unexpected(ErasedTemporaries.error()); + return std::unexpected(std::move(Failure)); + }; + + for (std::size_t Index = 0; Index < Rule.Instructions.size(); ++Index) { + const InstructionDescriptor *Descriptor = + Target.instructionDescriptor(Rule.Instructions[Index].ResultOpcode); + if (Descriptor == nullptr) + return Rollback(Error(ErrorCode::VerificationError, + "generated match table selects an unknown instruction opcode")); + auto Constraint = constrainRenderedInstruction(*Descriptor, RenderedInstructions[Index], + Function, Target, ConstraintSnapshots); + if (!Constraint) + return Rollback(Constraint.error()); } - if (!Descriptor->Variadic && Descriptor->Operands.size() != NewExplicitCount) { - return makeUnexpected(ErrorCode::VerificationError, - "generated match table result has an incompatible operand count"); + MachineIRBuilder Builder(Function, Target); + MachineInstr *Anchor = &Instruction; + for (std::size_t Index = 1; Index < Rule.Instructions.size(); ++Index) { + auto Point = Builder.setInsertPointAfter(*Anchor); + if (!Point) + return Rollback(Point.error()); + auto Pending = Builder.buildInstr(Rule.Instructions[Index].ResultOpcode); + if (!Pending) + return Rollback(Pending.error()); + for (MachineOperand &Operand : RenderedInstructions[Index]) { + auto Added = Pending->addOperand(std::move(Operand)); + if (!Added) + return Rollback(Added.error()); + } + auto Built = Pending->build(); + if (!Built) + return Rollback(Built.error()); + AddedInstructions.push_back(*Built); + auto Finalized = Finalize(**Built, InputFlags, InputMayRaiseFPException); + if (!Finalized) + return Rollback(Finalized.error()); + Anchor = *Built; } - auto SetOpcode = Instruction.setOpcode(Rule.ResultOpcode); + const MatchedRule::Instruction &RootAction = Rule.Instructions.front(); + RootModified = true; + auto SetOpcode = Instruction.setOpcode(RootAction.ResultOpcode); if (!SetOpcode) - return std::unexpected(SetOpcode.error()); + return Rollback(SetOpcode.error()); while (Instruction.operandCount() != 0) { auto Erased = Instruction.eraseOperand(Instruction.operandCount() - 1U); - if (!Erased) { - auto Restored = restoreInstruction(Instruction, OriginalOpcode, OriginalOperands, Ties); - if (!Restored) - return std::unexpected(Restored.error()); - return std::unexpected(Erased.error()); - } + if (!Erased) + return Rollback(Erased.error()); } - for (MachineOperand &Operand : NewOperands) { + for (MachineOperand &Operand : RenderedInstructions.front()) { auto Added = Instruction.addOperand(std::move(Operand)); - if (!Added) { - auto Restored = restoreInstruction(Instruction, OriginalOpcode, OriginalOperands, Ties); - if (!Restored) - return std::unexpected(Restored.error()); - return std::unexpected(Added.error()); - } + if (!Added) + return Rollback(Added.error()); + } + for (std::size_t Index = OriginalExplicitCount; Index < OriginalOperandCount; ++Index) { + SourceToOutput[Index] = Instruction.operandCount(); + auto Added = Instruction.addOperand(OriginalOperands[Index].cloneDetached()); + if (!Added) + return Rollback(Added.error()); } - for (const auto [OldDef, OldUse] : Ties) { const std::optional<std::size_t> &NewDef = SourceToOutput[OldDef]; const std::optional<std::size_t> &NewUse = SourceToOutput[OldUse]; if (!NewDef || !NewUse) continue; - auto Tied = Instruction.tieOperands(NewDef.value(), NewUse.value()); - if (!Tied) { - auto Restored = restoreInstruction(Instruction, OriginalOpcode, OriginalOperands, Ties); - if (!Restored) - return std::unexpected(Restored.error()); - return std::unexpected(Tied.error()); - } + auto Tied = Instruction.tieOperands(*NewDef, *NewUse); + if (!Tied) + return Rollback(Tied.error()); } for (const Register Reg : Rule.EraseOperandDefinitions) { MachineInstr *Definition = Function.registerInfo().getVRegDef(Reg); if (Definition == nullptr || Definition == &Instruction || - !isTriviallyDead(*Definition, Function.registerInfo(), Target)) + !isTriviallyDead(*Definition, Function.registerInfo(), Target)) { continue; + } auto Erased = eraseInstr(*Definition, Function.registerInfo(), Target); if (!Erased) - return std::unexpected(Erased.error()); + return Rollback(Erased.error()); } return {}; } } // namespace +Expected<void> GIMatchTableExecutor::finalizeGeneratedInstruction( + MachineInstr &, MachineFunction &, const TargetDescription &, MachineInstrFlags, bool) const { + return {}; +} + Expected<bool> GIMatchTableExecutor::match(const GIMatchTable &Table, const MachineInstr &Instruction, const MachineFunction &Function, @@ -478,9 +891,11 @@ Expected<bool> GIMatchTableExecutor::match(const GIMatchTable &Table, return std::unexpected(Match.error()); if (!*Match) continue; - if (Target.instructionDescriptor((*Match)->ResultOpcode) == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "generated match table selects an unknown instruction opcode"); + for (const MatchedRule::Instruction &Action : (*Match)->Instructions) { + if (Target.instructionDescriptor(Action.ResultOpcode) == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table selects an unknown instruction opcode"); + } } return true; } @@ -496,7 +911,13 @@ Expected<bool> GIMatchTableExecutor::select(const GIMatchTable &Table, MachineIn return std::unexpected(Match.error()); if (!*Match) continue; - auto Applied = applyRule(**Match, Instruction, Function, Target); + auto Applied = applyRule( + **Match, Instruction, Function, Target, + [this, &Function, &Target](MachineInstr &Output, MachineInstrFlags InputFlags, + bool InputMayRaiseFPException) { + return finalizeGeneratedInstruction(Output, Function, Target, InputFlags, + InputMayRaiseFPException); + }); if (!Applied) return std::unexpected(Applied.error()); return true; diff --git a/backend/lib/CodeGen/GlobalISel/InstructionSelect.cpp b/backend/lib/CodeGen/GlobalISel/InstructionSelect.cpp index 1f7f453..2e61d8e 100644 --- a/backend/lib/CodeGen/GlobalISel/InstructionSelect.cpp +++ b/backend/lib/CodeGen/GlobalISel/InstructionSelect.cpp @@ -199,6 +199,16 @@ Expected<void> InstructionSelector::propagateSelectionFlags(MachineInstr &Output Target); } +Expected<void> InstructionSelector::finalizeGeneratedInstruction( + MachineInstr &Output, MachineFunction &Function, const TargetDescription &Target, + MachineInstrFlags InputFlags, bool InputMayRaiseFPException) const { + auto Flags = + propagateSelectionFlags(Output, InputFlags, InputMayRaiseFPException, Target); + if (!Flags) + return std::unexpected(Flags.error()); + return constrainSelectedInstruction(Output, Function, Target); +} + const MachinePassContract &InstructionSelect::contract() const noexcept { return instructionSelectContract(); } diff --git a/backend/lib/CodeGen/TargetDescription.cpp b/backend/lib/CodeGen/TargetDescription.cpp index a867a5b..ab77965 100644 --- a/backend/lib/CodeGen/TargetDescription.cpp +++ b/backend/lib/CodeGen/TargetDescription.cpp @@ -17,6 +17,7 @@ #include <optional> #include <ranges> #include <span> +#include <string> #include <utility> #include <vector> @@ -342,8 +343,12 @@ Expected<void> TargetDescription::validateTargetOperand(const OperandDescriptor if (Descriptor != nullptr && Descriptor->RegClass) { const auto EffectiveClass = effectiveRegisterClass(Operand, RegInfo); if (!EffectiveClass || !Registers.isSubclass(*EffectiveClass, *Descriptor->RegClass)) { - return makeUnexpected(ErrorCode::VerificationError, - "virtual register class does not satisfy the operand descriptor"); + const std::string ActualClass = + EffectiveClass ? std::to_string(EffectiveClass->value()) : "<none>"; + return makeUnexpected( + ErrorCode::VerificationError, + "virtual register class " + ActualClass + " does not satisfy required class " + + std::to_string(Descriptor->RegClass->value())); } } return {}; @@ -464,7 +469,9 @@ TargetDescription::validateExplicitOperands(Opcode InstructionOpcode, auto TargetValidation = validateTargetOperand(&descriptorForOperand(*Descriptor, Index), Operands[Index], RegInfo); if (!TargetValidation) - return std::unexpected(TargetValidation.error()); + return std::unexpected(withErrorContext( + TargetValidation.error(), "operand " + std::to_string(Index) + " of '" + + Descriptor->Name + "': ")); } return {}; } diff --git a/backend/lib/TableGen/Parser.cpp b/backend/lib/TableGen/Parser.cpp index 9807534..c194448 100644 --- a/backend/lib/TableGen/Parser.cpp +++ b/backend/lib/TableGen/Parser.cpp @@ -1180,25 +1180,53 @@ private: for (const yaml::NodeID ElementID : Node.sequence()) { const yaml::Node *Element = Input.node(ElementID); if (!requireMapping(Element, "selection operand definition constant") || - !checkKeys(*Element, {"index", "value"}, "selection operand definition constant")) { + !checkKeys(*Element, {"index", "ranges"}, "selection operand definition constant")) { return false; } const yaml::Node *IndexNode = requiredNode(*Element, "index", "selection operand definition constant"); - const yaml::Node *ValueNode = - requiredNode(*Element, "value", "selection operand definition constant"); - if (IndexNode == nullptr || ValueNode == nullptr) + const yaml::Node *RangesNode = + requiredNode(*Element, "ranges", "selection operand definition constant"); + if (IndexNode == nullptr || RangesNode == nullptr) return false; const auto Index = unsignedInteger32(*IndexNode, "selection operand definition constant index"); - const auto Value = signedInteger(*ValueNode, "selection operand definition constant value"); - if (!Index || !Value) + if (!Index || !requireSequence(RangesNode, "selection operand definition constant ranges") || + RangesNode->sequence().empty()) { return false; - Result.push_back({ + } + + SourceSelectionOperandDefConstantPredicate Predicate{ .OperandIndex = *Index, - .Value = *Value, + .Ranges = {}, .Range = Element->range(), - }); + }; + for (const yaml::NodeID RangeID : RangesNode->sequence()) { + const yaml::Node *Range = Input.node(RangeID); + if (!requireMapping(Range, "selection operand definition constant range") || + !checkKeys(*Range, {"min", "max"}, "selection operand definition constant range")) { + return false; + } + const yaml::Node *MinimumNode = + requiredNode(*Range, "min", "selection operand definition constant range"); + const yaml::Node *MaximumNode = + requiredNode(*Range, "max", "selection operand definition constant range"); + if (MinimumNode == nullptr || MaximumNode == nullptr) + return false; + const auto Minimum = signedInteger(*MinimumNode, "selection constant range minimum"); + const auto Maximum = signedInteger(*MaximumNode, "selection constant range maximum"); + if (!Minimum || !Maximum || *Minimum > *Maximum) { + if (Minimum && Maximum) + fail(Range->range(), "selection operand definition constant range is invalid"); + return false; + } + Predicate.Ranges.push_back({ + .Minimum = *Minimum, + .Maximum = *Maximum, + .SourceRange = Range->range(), + }); + } + Result.push_back(std::move(Predicate)); } return true; } @@ -1211,20 +1239,33 @@ private: for (const yaml::NodeID ElementID : Node.sequence()) { const yaml::Node *Element = Input.node(ElementID); if (!requireMapping(Element, "selection operand renderer") || - !checkKeys(*Element, {"operand", "physical_register", "flags"}, + !checkKeys(*Element, + {"operand", "physical_register", "temporary", "captured_immediate", + "immediate", "transform", "flags"}, "selection operand renderer")) { return false; } const yaml::Node *OperandNode = optionalNode(*Element, "operand"); const yaml::Node *PhysicalRegisterNode = optionalNode(*Element, "physical_register"); - if ((OperandNode == nullptr) == (PhysicalRegisterNode == nullptr)) { - fail(Element->range(), "selection operand renderer requires exactly one of 'operand' or " - "'physical_register'"); + const yaml::Node *TemporaryNode = optionalNode(*Element, "temporary"); + const yaml::Node *CapturedImmediateNode = optionalNode(*Element, "captured_immediate"); + const yaml::Node *ImmediateNode = optionalNode(*Element, "immediate"); + const std::uint32_t SourceCount = static_cast<std::uint32_t>(OperandNode != nullptr) + + static_cast<std::uint32_t>(PhysicalRegisterNode != nullptr) + + static_cast<std::uint32_t>(TemporaryNode != nullptr) + + static_cast<std::uint32_t>(CapturedImmediateNode != nullptr) + + static_cast<std::uint32_t>(ImmediateNode != nullptr); + if (SourceCount != 1) { + fail(Element->range(), "selection operand renderer requires exactly one source"); return false; } SourceSelectionOperandRenderer Renderer{ .OperandIndex = std::nullopt, .PhysicalRegisterSymbol = {}, + .TemporarySymbol = {}, + .CapturedImmediateOperand = std::nullopt, + .Immediate = std::nullopt, + .ImmediateTransform = "identity", .Flags = {}, .Range = Element->range(), }; @@ -1233,12 +1274,38 @@ private: if (!Index) return false; Renderer.OperandIndex = Index; - } else { + } else if (PhysicalRegisterNode != nullptr) { const auto Symbol = scalar(*PhysicalRegisterNode, "selection operand renderer physical register"); if (!Symbol) return false; Renderer.PhysicalRegisterSymbol = *Symbol; + } else if (TemporaryNode != nullptr) { + const auto Symbol = scalar(*TemporaryNode, "selection operand renderer temporary"); + if (!Symbol) + return false; + Renderer.TemporarySymbol = *Symbol; + } else if (CapturedImmediateNode != nullptr) { + const auto Index = + unsignedInteger32(*CapturedImmediateNode, "selection operand renderer captured immediate"); + if (!Index) + return false; + Renderer.CapturedImmediateOperand = *Index; + } else { + const auto Value = signedInteger(*ImmediateNode, "selection operand renderer immediate"); + if (!Value) + return false; + Renderer.Immediate = *Value; + } + if (const yaml::Node *TransformNode = optionalNode(*Element, "transform")) { + const auto Transform = scalar(*TransformNode, "selection operand renderer transform"); + if (!Transform) + return false; + Renderer.ImmediateTransform = *Transform; + } + if (!Renderer.CapturedImmediateOperand && Renderer.ImmediateTransform != "identity") { + fail(Element->range(), "selection operand renderer transform requires a captured immediate"); + return false; } if (const yaml::Node *FlagsNode = optionalNode(*Element, "flags")) { const auto Flags = scalarSequence(*FlagsNode, "selection operand renderer flags"); @@ -1251,6 +1318,63 @@ private: return true; } + [[nodiscard]] bool parseSelectionTemporaries(const yaml::Node &Node, + std::vector<SourceSelectionTemporary> &Result) { + if (!requireSequence(&Node, "selection temporaries")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection temporary") || + !checkKeys(*Element, {"id", "type_from_operand"}, "selection temporary")) { + return false; + } + const auto Symbol = requiredScalar(*Element, "id", "selection temporary"); + const yaml::Node *TypeNode = requiredNode(*Element, "type_from_operand", "selection temporary"); + if (!Symbol || TypeNode == nullptr) + return false; + const auto TypeFromOperand = unsignedInteger32(*TypeNode, "selection temporary type operand"); + if (!TypeFromOperand) + return false; + Result.push_back({ + .Symbol = *Symbol, + .TypeFromOperand = *TypeFromOperand, + .Range = Element->range(), + }); + } + return true; + } + + [[nodiscard]] bool parseSelectionActions(const yaml::Node &Node, + std::vector<SourceSelectionAction> &Result) { + if (!requireSequence(&Node, "selection instructions")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection instruction") || + !checkKeys(*Element, {"opcode", "root", "operands"}, "selection instruction")) { + return false; + } + const auto Opcode = requiredScalar(*Element, "opcode", "selection instruction"); + const yaml::Node *RootNode = requiredNode(*Element, "root", "selection instruction"); + const yaml::Node *OperandsNode = requiredNode(*Element, "operands", "selection instruction"); + if (!Opcode || RootNode == nullptr || OperandsNode == nullptr) + return false; + const auto ReplacesRoot = boolean(*RootNode, "selection instruction root"); + if (!ReplacesRoot) + return false; + std::vector<SourceSelectionOperandRenderer> Renderers; + if (!parseSelectionOperandRenderers(*OperandsNode, Renderers)) + return false; + Result.push_back({ + .ResultOpcodeSymbol = *Opcode, + .ReplacesRoot = *ReplacesRoot, + .OperandRenderers = std::move(Renderers), + .Range = Element->range(), + }); + } + return true; + } + [[nodiscard]] bool parseSelectionRuleMatch(const yaml::Node &Node, SourceSelectionRule &Result) { if (!requireMapping(&Node, "selection rule match") || !checkKeys(Node, @@ -1294,26 +1418,19 @@ private: [[nodiscard]] bool parseSelectionRuleApply(const yaml::Node &Node, SourceSelectionRule &Result) { if (!requireMapping(&Node, "selection rule apply") || - !checkKeys(Node, {"opcode", "append_immediates", "operands", "erase_operand_definitions"}, + !checkKeys(Node, {"temporaries", "instructions", "erase_operand_definitions"}, "selection rule apply")) { return false; } - const auto Opcode = requiredScalar(Node, "opcode", "selection rule apply"); - if (!Opcode) + const yaml::Node *Instructions = requiredNode(Node, "instructions", "selection rule apply"); + if (Instructions == nullptr) return false; - Result.ResultOpcodeSymbol = *Opcode; - if (const yaml::Node *Immediates = optionalNode(Node, "append_immediates")) { - auto Values = signedIntegerSequence(*Immediates, "selection rule appended immediates"); - if (!Values) + if (const yaml::Node *Temporaries = optionalNode(Node, "temporaries")) { + if (!parseSelectionTemporaries(*Temporaries, Result.Temporaries)) return false; - Result.AppendedImmediates = std::move(*Values); - } - if (const yaml::Node *Operands = optionalNode(Node, "operands")) { - std::vector<SourceSelectionOperandRenderer> Renderers; - if (!parseSelectionOperandRenderers(*Operands, Renderers)) - return false; - Result.OperandRenderers = std::move(Renderers); } + if (!parseSelectionActions(*Instructions, Result.Actions)) + return false; if (const yaml::Node *Erase = optionalNode(Node, "erase_operand_definitions")) { auto Indices = unsignedInteger32Sequence(*Erase, "selection operand definitions"); if (!Indices) diff --git a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp index 68574a3..38ef1af 100644 --- a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp +++ b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp @@ -268,7 +268,6 @@ public: case GenericOpcode::G_SEXT: case GenericOpcode::G_SEXT_INREG: case GenericOpcode::G_ZEXT: - case GenericOpcode::G_ICMP: case GenericOpcode::G_FCMP: case GenericOpcode::G_FPEXT: case GenericOpcode::G_FPTRUNC: @@ -476,8 +475,6 @@ public: return Instruction.setOpcode(opcode(GenericOpcode::COPY)); case GenericOpcode::G_SEXT_INREG: return selectSignExtendInRegister(Instruction, Function, Target, Type); - case GenericOpcode::G_ICMP: - return selectIntegerCompare(Instruction, Function, Target); case GenericOpcode::G_FCMP: return selectFloatingCompare(Instruction, Function, Target); case GenericOpcode::G_FPEXT: @@ -748,9 +745,9 @@ private: const TargetDescription &Target, Register Source, LowLevelType Type, bool Signed) const { - if (!isSupportedIntegerScalar(Type)) + if (!isSupportedIntegerType(Type)) return makeUnexpected(ErrorCode::Unsupported, - "RISC-V integer normalization requires a scalar i1-i64 type"); + "RISC-V integer normalization requires an integer scalar or a 64-bit pointer"); const std::uint32_t Bits = Type.scalarBitWidth(); if (Bits == 64) return Source; @@ -1741,175 +1738,6 @@ private: return {}; } - [[nodiscard]] Expected<void> selectIntegerCompare(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) const { - const MachineOperand *PredicateMO = Instruction.operandAt(1); - const auto *Predicate = - PredicateMO == nullptr ? nullptr : PredicateMO->getIf<PredicateOperand>(); - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *LeftOperand = Instruction.operandAt(2); - const MachineOperand *RightOperand = Instruction.operandAt(3); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - const std::optional<LowLevelType> LeftType = operandType(Function, Instruction, 2); - if (Predicate == nullptr || Predicate->integerPredicate() == nullptr || - Destination == nullptr || Left == nullptr || Right == nullptr || !LeftType) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V integer comparison has invalid operands"); - const IntegerPredicate Condition = *Predicate->integerPredicate(); - const Register DestinationRegister = *Destination; - const bool SignedOperands = Condition == IntegerPredicate::SignedGreaterThan || - Condition == IntegerPredicate::SignedGreaterEqual || - Condition == IntegerPredicate::SignedLessThan || - Condition == IntegerPredicate::SignedLessEqual; - auto LeftRegister = - normalizeIntegerBefore(Instruction, Function, Target, *Left, *LeftType, SignedOperands); - auto RightRegister = - normalizeIntegerBefore(Instruction, Function, Target, *Right, *LeftType, SignedOperands); - if (!LeftRegister || !RightRegister) - return std::unexpected(!LeftRegister ? LeftRegister.error() : RightRegister.error()); - auto ErasePredicate = Instruction.eraseOperand(1); - if (!ErasePredicate) - return std::unexpected(ErasePredicate.error()); - - const auto SetSources = [&Instruction](Register NewLeft, Register NewRight) -> Expected<void> { - auto SetLeft = Instruction.operandAt(1)->setRegister(NewLeft); - if (!SetLeft) - return std::unexpected(SetLeft.error()); - return Instruction.operandAt(2)->setRegister(NewRight); - }; - const auto StrictOpcode = [](IntegerPredicate Value) -> std::optional<InstructionOpcode> { - switch (Value) { - case IntegerPredicate::SignedLessThan: - return InstructionOpcode::SLT; - case IntegerPredicate::UnsignedLessThan: - return InstructionOpcode::SLTU; - default: - return std::nullopt; - } - }; - - IntegerPredicate StrictCondition = Condition; - bool Swap = false; - bool Invert = false; - switch (Condition) { - case IntegerPredicate::Equal: - case IntegerPredicate::NotEqual: - break; - case IntegerPredicate::SignedGreaterThan: - StrictCondition = IntegerPredicate::SignedLessThan; - Swap = true; - break; - case IntegerPredicate::SignedGreaterEqual: - StrictCondition = IntegerPredicate::SignedLessThan; - Invert = true; - break; - case IntegerPredicate::SignedLessThan: - StrictCondition = IntegerPredicate::SignedLessThan; - break; - case IntegerPredicate::SignedLessEqual: - StrictCondition = IntegerPredicate::SignedLessThan; - Swap = true; - Invert = true; - break; - case IntegerPredicate::UnsignedGreaterThan: - StrictCondition = IntegerPredicate::UnsignedLessThan; - Swap = true; - break; - case IntegerPredicate::UnsignedGreaterEqual: - StrictCondition = IntegerPredicate::UnsignedLessThan; - Invert = true; - break; - case IntegerPredicate::UnsignedLessThan: - StrictCondition = IntegerPredicate::UnsignedLessThan; - break; - case IntegerPredicate::UnsignedLessEqual: - StrictCondition = IntegerPredicate::UnsignedLessThan; - Swap = true; - Invert = true; - break; - case IntegerPredicate::Count: - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V integer comparison predicate is invalid"); - } - - MachineIRBuilder Builder(Function, Target); - if (Condition == IntegerPredicate::Equal || Condition == IntegerPredicate::NotEqual) { - auto Temporary = createTemporary(Function, *LeftType, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto SetDestination = Instruction.operandAt(0)->setRegister(*Temporary); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto Set = SetSources(*LeftRegister, *RightRegister); - if (!Set) - return std::unexpected(Set.error()); - auto SetOpcode = Instruction.setOpcode(instructionOpcode(InstructionOpcode::XOR)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - if (Condition == IntegerPredicate::Equal) { - auto Result = buildImmediateAfter(Builder, Instruction, InstructionOpcode::SLTIU, - DestinationRegister, *Temporary, 1); - if (!Result) - return std::unexpected(Result.error()); - } else { - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto Result = - buildBinaryAfter(Builder, Instruction, InstructionOpcode::SLTU, DestinationRegister, - Register::fromPhysical(*Zero), *Temporary); - if (!Result) - return std::unexpected(Result.error()); - } - return {}; - } - - const std::optional<InstructionOpcode> OpcodeValue = StrictOpcode(StrictCondition); - if (!OpcodeValue) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V integer comparison predicate is invalid"); - if (Invert) { - auto Temporary = createTemporary(Function, *LeftType, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto SetDestination = Instruction.operandAt(0)->setRegister(*Temporary); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - const Register Base = *Temporary; - if (Swap) { - auto Set = SetSources(*RightRegister, *LeftRegister); - if (!Set) - return std::unexpected(Set.error()); - } else { - auto Set = SetSources(*LeftRegister, *RightRegister); - if (!Set) - return std::unexpected(Set.error()); - } - auto SetOpcode = Instruction.setOpcode(instructionOpcode(*OpcodeValue)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - auto Result = buildImmediateAfter(Builder, Instruction, InstructionOpcode::XORI, - DestinationRegister, Base, 1); - if (!Result) - return std::unexpected(Result.error()); - return {}; - } - if (Swap) { - auto Set = SetSources(*RightRegister, *LeftRegister); - if (!Set) - return std::unexpected(Set.error()); - } else { - auto Set = SetSources(*LeftRegister, *RightRegister); - if (!Set) - return std::unexpected(Set.error()); - } - return Instruction.setOpcode(instructionOpcode(*OpcodeValue)); - } - [[nodiscard]] Expected<void> selectFloatingCompare(MachineInstr &Instruction, MachineFunction &Function, const TargetDescription &Target) const { diff --git a/backend/lib/Target/RISCV/RISCVGISel.rccdesc b/backend/lib/Target/RISCV/RISCVGISel.rccdesc index bd946be..d8b0a42 100644 --- a/backend/lib/Target/RISCV/RISCVGISel.rccdesc +++ b/backend/lib/Target/RISCV/RISCVGISel.rccdesc @@ -12,7 +12,11 @@ selection_rules: - { index: 0, bank: GPR } - { index: 1, bank: GPR } - { index: 2, bank: GPR } - apply: { opcode: SLLW } + apply: + instructions: + - opcode: SLLW + root: true + operands: [{ operand: 0 }, { operand: 1 }, { operand: 2 }] - id: select_g_sraw priority: 100 @@ -26,7 +30,11 @@ selection_rules: - { index: 0, bank: GPR } - { index: 1, bank: GPR } - { index: 2, bank: GPR } - apply: { opcode: SRAW } + apply: + instructions: + - opcode: SRAW + root: true + operands: [{ operand: 0 }, { operand: 1 }, { operand: 2 }] - id: select_g_srlw priority: 100 @@ -40,7 +48,11 @@ selection_rules: - { index: 0, bank: GPR } - { index: 1, bank: GPR } - { index: 2, bank: GPR } - apply: { opcode: SRLW } + apply: + instructions: + - opcode: SRLW + root: true + operands: [{ operand: 0 }, { operand: 1 }, { operand: 2 }] - id: select_g_divw priority: 100 @@ -54,7 +66,11 @@ selection_rules: - { index: 0, bank: GPR } - { index: 1, bank: GPR } - { index: 2, bank: GPR } - apply: { opcode: DIVW } + apply: + instructions: + - opcode: DIVW + root: true + operands: [{ operand: 0 }, { operand: 1 }, { operand: 2 }] - id: select_g_divuw priority: 100 @@ -68,7 +84,11 @@ selection_rules: - { index: 0, bank: GPR } - { index: 1, bank: GPR } - { index: 2, bank: GPR } - apply: { opcode: DIVUW } + apply: + instructions: + - opcode: DIVUW + root: true + operands: [{ operand: 0 }, { operand: 1 }, { operand: 2 }] - id: select_g_remuw priority: 100 @@ -82,7 +102,11 @@ selection_rules: - { index: 0, bank: GPR } - { index: 1, bank: GPR } - { index: 2, bank: GPR } - apply: { opcode: REMUW } + apply: + instructions: + - opcode: REMUW + root: true + operands: [{ operand: 0 }, { operand: 1 }, { operand: 2 }] - id: select_g_fcvt_w_rv64_s32 priority: 100 @@ -94,7 +118,11 @@ selection_rules: operand_banks: - { index: 0, bank: GPR } - { index: 1, bank: FPR } - apply: { opcode: FCVT_W_S } + apply: + instructions: + - opcode: FCVT_W_S + root: true + operands: [{ operand: 0 }, { operand: 1 }, { operand: 2 }] - id: select_g_fcvt_w_rv64_s64 priority: 100 @@ -106,7 +134,11 @@ selection_rules: operand_banks: - { index: 0, bank: GPR } - { index: 1, bank: FPR } - apply: { opcode: FCVT_W_D } + apply: + instructions: + - opcode: FCVT_W_D + root: true + operands: [{ operand: 0 }, { operand: 1 }, { operand: 2 }] - id: select_g_fcvt_wu_rv64_s32 priority: 100 @@ -118,7 +150,11 @@ selection_rules: operand_banks: - { index: 0, bank: GPR } - { index: 1, bank: FPR } - apply: { opcode: FCVT_WU_S } + apply: + instructions: + - opcode: FCVT_WU_S + root: true + operands: [{ operand: 0 }, { operand: 1 }, { operand: 2 }] - id: select_g_fcvt_wu_rv64_s64 priority: 100 @@ -130,7 +166,11 @@ selection_rules: operand_banks: - { index: 0, bank: GPR } - { index: 1, bank: FPR } - apply: { opcode: FCVT_WU_D } + apply: + instructions: + - opcode: FCVT_WU_D + root: true + operands: [{ operand: 0 }, { operand: 1 }, { operand: 2 }] - id: select_g_fclass_s32 priority: 100 @@ -142,7 +182,11 @@ selection_rules: operand_banks: - { index: 0, bank: GPR } - { index: 1, bank: FPR } - apply: { opcode: FCLASS_S } + apply: + instructions: + - opcode: FCLASS_S + root: true + operands: [{ operand: 0 }, { operand: 1 }] - id: select_g_fclass_s64 priority: 100 @@ -154,10 +198,207 @@ selection_rules: operand_banks: - { index: 0, bank: GPR } - { index: 1, bank: FPR } - apply: { opcode: FCLASS_D } + apply: + instructions: + - opcode: FCLASS_D + root: true + operands: [{ operand: 0 }, { operand: 1 }] + - + id: select_g_icmp_eq_zero + priority: 500 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: eq } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: [{ min: 0, max: 0 }] + apply: + instructions: + - opcode: SLTIU + root: true + operands: [{ operand: 0 }, { operand: 2 }, { immediate: 1 }] + erase_operand_definitions: [3] - id: select_g_icmp_ne_zero - priority: 200 + priority: 500 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: ne } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: [{ min: 0, max: 0 }] + apply: + instructions: + - opcode: SLTU + root: true + operands: + - { operand: 0 } + - { physical_register: X0, flags: [use] } + - { operand: 2 } + erase_operand_definitions: [3] + - + id: select_g_icmp_ult_immediate + priority: 400 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: ult } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: [{ min: -2048, max: 2047 }] + apply: + instructions: + - opcode: SLTIU + root: true + operands: + - { operand: 0 } + - { operand: 2 } + - { captured_immediate: 3 } + erase_operand_definitions: [3] + - + id: select_g_icmp_slt_immediate + priority: 400 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: slt } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: [{ min: -2048, max: 2047 }] + apply: + instructions: + - opcode: SLTI + root: true + operands: + - { operand: 0 } + - { operand: 2 } + - { captured_immediate: 3 } + erase_operand_definitions: [3] + - + id: select_g_icmp_ugt_immediate + priority: 400 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: ugt } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: + - { min: -2049, max: -2 } + - { min: 1, max: 2046 } + apply: + temporaries: + - { id: compare, type_from_operand: 2 } + instructions: + - opcode: SLTIU + root: true + operands: + - { temporary: compare, flags: [define] } + - { operand: 2 } + - { captured_immediate: 3, transform: plus_one } + - opcode: XORI + root: false + operands: + - { operand: 0 } + - { temporary: compare, flags: [use] } + - { immediate: 1 } + erase_operand_definitions: [3] + - + id: select_g_icmp_sgt_immediate + priority: 400 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: sgt } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: + - { min: -2049, max: -1 } + - { min: 1, max: 2046 } + apply: + temporaries: + - { id: compare, type_from_operand: 2 } + instructions: + - opcode: SLTI + root: true + operands: + - { temporary: compare, flags: [define] } + - { operand: 2 } + - { captured_immediate: 3, transform: plus_one } + - opcode: XORI + root: false + operands: + - { operand: 0 } + - { temporary: compare, flags: [use] } + - { immediate: 1 } + erase_operand_definitions: [3] + - + id: select_g_icmp_eq_immediate + priority: 400 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: eq } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: [{ min: -2047, max: 2048 }] + apply: + temporaries: + - { id: difference, type_from_operand: 2 } + instructions: + - opcode: ADDI + root: true + operands: + - { temporary: difference, flags: [define] } + - { operand: 2 } + - { captured_immediate: 3, transform: negate } + - opcode: SLTIU + root: false + operands: + - { operand: 0 } + - { temporary: difference, flags: [use] } + - { immediate: 1 } + erase_operand_definitions: [3] + - + id: select_g_icmp_ne_immediate + priority: 400 match: opcode: G_ICMP result_type: { kind: scalar, bits: 64 } @@ -168,11 +409,384 @@ selection_rules: - { index: 0, bank: GPR } - { index: 2, bank: GPR } operand_def_constants: - - { index: 3, value: 0 } + - index: 3 + ranges: [{ min: -2047, max: 2048 }] + apply: + temporaries: + - { id: difference, type_from_operand: 2 } + instructions: + - opcode: ADDI + root: true + operands: + - { temporary: difference, flags: [define] } + - { operand: 2 } + - { captured_immediate: 3, transform: negate } + - opcode: SLTU + root: false + operands: + - { operand: 0 } + - { physical_register: X0, flags: [use] } + - { temporary: difference, flags: [use] } + erase_operand_definitions: [3] + - + id: select_g_icmp_ule_immediate + priority: 400 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: ule } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: + - { min: -2049, max: -2 } + - { min: 1, max: 2046 } + apply: + instructions: + - opcode: SLTIU + root: true + operands: + - { operand: 0 } + - { operand: 2 } + - { captured_immediate: 3, transform: plus_one } + erase_operand_definitions: [3] + - + id: select_g_icmp_sle_immediate + priority: 400 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: sle } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: + - { min: -2049, max: -1 } + - { min: 1, max: 2046 } + apply: + instructions: + - opcode: SLTI + root: true + operands: + - { operand: 0 } + - { operand: 2 } + - { captured_immediate: 3, transform: plus_one } + erase_operand_definitions: [3] + - + id: select_g_icmp_uge_immediate + priority: 400 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: uge } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: [{ min: -2048, max: 2047 }] apply: - opcode: SLTU - operands: - - { operand: 0 } - - { physical_register: X0, flags: [use] } - - { operand: 2 } + temporaries: + - { id: compare, type_from_operand: 2 } + instructions: + - opcode: SLTIU + root: true + operands: + - { temporary: compare, flags: [define] } + - { operand: 2 } + - { captured_immediate: 3 } + - opcode: XORI + root: false + operands: + - { operand: 0 } + - { temporary: compare, flags: [use] } + - { immediate: 1 } erase_operand_definitions: [3] + - + id: select_g_icmp_sge_immediate + priority: 400 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: sge } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + operand_def_constants: + - index: 3 + ranges: [{ min: -2048, max: 2047 }] + apply: + temporaries: + - { id: compare, type_from_operand: 2 } + instructions: + - opcode: SLTI + root: true + operands: + - { temporary: compare, flags: [define] } + - { operand: 2 } + - { captured_immediate: 3 } + - opcode: XORI + root: false + operands: + - { operand: 0 } + - { temporary: compare, flags: [use] } + - { immediate: 1 } + erase_operand_definitions: [3] + - + id: select_g_icmp_ult_register + priority: 100 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: ult } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + - { index: 3, bank: GPR } + apply: + instructions: + - opcode: SLTU + root: true + operands: [{ operand: 0 }, { operand: 2 }, { operand: 3 }] + - + id: select_g_icmp_slt_register + priority: 100 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: slt } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + - { index: 3, bank: GPR } + apply: + instructions: + - opcode: SLT + root: true + operands: [{ operand: 0 }, { operand: 2 }, { operand: 3 }] + - + id: select_g_icmp_ugt_register + priority: 100 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: ugt } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + - { index: 3, bank: GPR } + apply: + instructions: + - opcode: SLTU + root: true + operands: [{ operand: 0 }, { operand: 3 }, { operand: 2 }] + - + id: select_g_icmp_sgt_register + priority: 100 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: sgt } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + - { index: 3, bank: GPR } + apply: + instructions: + - opcode: SLT + root: true + operands: [{ operand: 0 }, { operand: 3 }, { operand: 2 }] + - + id: select_g_icmp_eq_register + priority: 100 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: eq } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + - { index: 3, bank: GPR } + apply: + temporaries: + - { id: difference, type_from_operand: 2 } + instructions: + - opcode: XOR + root: true + operands: + - { temporary: difference, flags: [define] } + - { operand: 2 } + - { operand: 3 } + - opcode: SLTIU + root: false + operands: + - { operand: 0 } + - { temporary: difference, flags: [use] } + - { immediate: 1 } + - + id: select_g_icmp_ne_register + priority: 100 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: ne } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + - { index: 3, bank: GPR } + apply: + temporaries: + - { id: difference, type_from_operand: 2 } + instructions: + - opcode: XOR + root: true + operands: + - { temporary: difference, flags: [define] } + - { operand: 2 } + - { operand: 3 } + - opcode: SLTU + root: false + operands: + - { operand: 0 } + - { physical_register: X0, flags: [use] } + - { temporary: difference, flags: [use] } + - + id: select_g_icmp_ule_register + priority: 100 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: ule } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + - { index: 3, bank: GPR } + apply: + temporaries: + - { id: compare, type_from_operand: 2 } + instructions: + - opcode: SLTU + root: true + operands: + - { temporary: compare, flags: [define] } + - { operand: 3 } + - { operand: 2 } + - opcode: XORI + root: false + operands: + - { operand: 0 } + - { temporary: compare, flags: [use] } + - { immediate: 1 } + - + id: select_g_icmp_sle_register + priority: 100 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: sle } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + - { index: 3, bank: GPR } + apply: + temporaries: + - { id: compare, type_from_operand: 2 } + instructions: + - opcode: SLT + root: true + operands: + - { temporary: compare, flags: [define] } + - { operand: 3 } + - { operand: 2 } + - opcode: XORI + root: false + operands: + - { operand: 0 } + - { temporary: compare, flags: [use] } + - { immediate: 1 } + - + id: select_g_icmp_uge_register + priority: 100 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: uge } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + - { index: 3, bank: GPR } + apply: + temporaries: + - { id: compare, type_from_operand: 2 } + instructions: + - opcode: SLTU + root: true + operands: + - { temporary: compare, flags: [define] } + - { operand: 2 } + - { operand: 3 } + - opcode: XORI + root: false + operands: + - { operand: 0 } + - { temporary: compare, flags: [use] } + - { immediate: 1 } + - + id: select_g_icmp_sge_register + priority: 100 + match: + opcode: G_ICMP + result_type: { kind: scalar, bits: 64 } + integer_predicate: { index: 1, value: sge } + operand_types: + - { index: 2, kind: any, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: GPR } + - { index: 3, bank: GPR } + apply: + temporaries: + - { id: compare, type_from_operand: 2 } + instructions: + - opcode: SLT + root: true + operands: + - { temporary: compare, flags: [define] } + - { operand: 2 } + - { operand: 3 } + - opcode: XORI + root: false + operands: + - { operand: 0 } + - { temporary: compare, flags: [use] } + - { immediate: 1 } |
