diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-19 03:19:40 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-19 03:19:40 +0800 |
| commit | 69e1baa931dfa63838683378fa20ed9c46c68a70 (patch) | |
| tree | d8ae9222ed52c3036b30371241bd593e0324ce6c | |
| parent | c0f2700e0c7effc5da267bf501232ff4bfea63bc (diff) | |
| download | rcc-project-69e1baa931dfa63838683378fa20ed9c46c68a70.tar.gz rcc-project-69e1baa931dfa63838683378fa20ed9c46c68a70.tar.xz | |
[RISCV][GlobalISel] Match floating comparisons
| -rw-r--r-- | backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h | 1 | ||||
| -rw-r--r-- | backend/include/rcc/TableGen/Record.h | 7 | ||||
| -rw-r--r-- | backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp | 26 | ||||
| -rw-r--r-- | backend/lib/CodeGen/GlobalISel/InstructionSelect.cpp | 34 | ||||
| -rw-r--r-- | backend/lib/TableGen/Parser.cpp | 32 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp | 344 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVGISelFloating.rccdesc | 114 | ||||
| -rw-r--r-- | backend/utils/TableGen/Common/CodeGenSelection.cpp | 54 | ||||
| -rw-r--r-- | backend/utils/TableGen/Common/CodeGenTarget.h | 6 | ||||
| -rw-r--r-- | backend/utils/TableGen/GlobalISelEmitter.cpp | 12 |
10 files changed, 471 insertions, 159 deletions
diff --git a/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h b/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h index a5b8b8e..a8b4f44 100644 --- a/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h +++ b/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h @@ -37,6 +37,7 @@ enum class GIMatchTableOpcode : std::uint8_t { CheckOperandBank, CheckOperandClass, CheckIntegerPredicate, + CheckFloatingPredicate, CheckTargetPredicate, CheckOperandImmediateRanges, CheckOperandDefConstantRanges, diff --git a/backend/include/rcc/TableGen/Record.h b/backend/include/rcc/TableGen/Record.h index 449cb92..7039d9d 100644 --- a/backend/include/rcc/TableGen/Record.h +++ b/backend/include/rcc/TableGen/Record.h @@ -236,6 +236,12 @@ struct SourceSelectionIntegerPredicate final { TextRange Range; }; +struct SourceSelectionFloatingPredicate final { + std::uint32_t OperandIndex = 0; + std::string Predicate; + TextRange Range; +}; + struct SourceSelectionTargetPredicate final { std::string Symbol; TextRange Range; @@ -307,6 +313,7 @@ struct SourceSelectionRule final { std::string MatchOpcodeSymbol; std::optional<SourceSelectionTypePredicate> ResultType; std::optional<SourceSelectionIntegerPredicate> IntegerPredicate; + std::optional<SourceSelectionFloatingPredicate> FloatingPredicate; std::vector<SourceSelectionTargetPredicate> TargetPredicates; std::vector<SourceSelectionOperandTypePredicate> OperandTypes; std::vector<SourceSelectionOperandBankPredicate> OperandBanks; diff --git a/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp b/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp index d4f73f6..3597509 100644 --- a/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp +++ b/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp @@ -106,6 +106,20 @@ namespace { return Integer != nullptr && static_cast<std::uint32_t>(*Integer) == PredicateValue; } +[[nodiscard]] Expected<bool> matchesFloatingPredicate(const MachineInstr &Instruction, + std::size_t OperandIndex, + std::uint32_t PredicateValue) { + if (PredicateValue >= static_cast<std::uint32_t>(FloatingPredicate::Count)) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table has an invalid floating predicate"); + } + const MachineOperand *Operand = Instruction.operandAt(OperandIndex); + const auto *Predicate = Operand == nullptr ? nullptr : Operand->getIf<PredicateOperand>(); + const FloatingPredicate *Floating = + Predicate == nullptr ? nullptr : Predicate->floatingPredicate(); + return Floating != nullptr && static_cast<std::uint32_t>(*Floating) == PredicateValue; +} + [[nodiscard]] std::optional<std::int64_t> immediateValue(const MachineOperand &Operand) { if (const std::int64_t *Immediate = Operand.getIf<std::int64_t>()) return *Immediate; @@ -384,6 +398,18 @@ matchRule(const GIMatchTable &Table, const GIMatchTableRule &Rule, const Machine return std::optional<MatchedRule>{}; break; } + case GIMatchTableOpcode::CheckFloatingPredicate: { + if (Applying) { + return makeUnexpected(ErrorCode::VerificationError, + "generated match table performs a predicate after an action"); + } + auto Matched = matchesFloatingPredicate(Instruction, Step.OperandIndex, Step.Value); + if (!Matched) + return std::unexpected(Matched.error()); + if (!*Matched) + return std::optional<MatchedRule>{}; + break; + } case GIMatchTableOpcode::CheckTargetPredicate: { if (Applying || Step.Symbol.empty()) { return makeUnexpected(ErrorCode::VerificationError, diff --git a/backend/lib/CodeGen/GlobalISel/InstructionSelect.cpp b/backend/lib/CodeGen/GlobalISel/InstructionSelect.cpp index 8cfb9c7..995a9a0 100644 --- a/backend/lib/CodeGen/GlobalISel/InstructionSelect.cpp +++ b/backend/lib/CodeGen/GlobalISel/InstructionSelect.cpp @@ -273,6 +273,19 @@ Expected<MachinePassResult> InstructionSelect::runImplementation(MachineFunction Worklist.push_back(Instruction); DiscoveredInstructions.insert(Instruction->stableID()); } + const auto DiscoverNewInstructions = [&]() -> Expected<void> { + for (MachineInstr *Candidate : bottomUpInstructions(Function)) { + const InstructionDescriptor *CandidateDescriptor = + Target.instructionDescriptor(Candidate->opcode()); + if (CandidateDescriptor == nullptr) { + return std::unexpected( + passFailure("instruction selector produced an unknown instruction descriptor")); + } + if (DiscoveredInstructions.insert(Candidate->stableID()).second) + Worklist.push_back(Candidate); + } + return {}; + }; for (std::size_t Index = 0; Index < Worklist.size(); ++Index) { MachineInstr *Instruction = Worklist[Index]; @@ -320,6 +333,14 @@ Expected<MachinePassResult> InstructionSelect::runImplementation(MachineFunction auto Selection = Info->select(*Instruction, Function, Target); if (!Selection) return std::unexpected(Selection.error()); + if (Instruction->parent() == nullptr) { + Result.Changed = true; + Result.Changes.add(MachinePassChange::Instructions); + auto Discovered = DiscoverNewInstructions(); + if (!Discovered) + return std::unexpected(Discovered.error()); + continue; + } const InstructionDescriptor *Selected = Target.instructionDescriptor(Instruction->opcode()); if (Selected == nullptr) return std::unexpected( @@ -348,16 +369,9 @@ Expected<MachinePassResult> InstructionSelect::runImplementation(MachineFunction Result.Changes.add(MachinePassChange::Instructions); if (Selected->Flags.has(InstructionFlag::Generic)) Worklist.push_back(Instruction); - for (MachineInstr *Candidate : bottomUpInstructions(Function)) { - const InstructionDescriptor *CandidateDescriptor = - Target.instructionDescriptor(Candidate->opcode()); - if (CandidateDescriptor == nullptr) - return std::unexpected( - passFailure("instruction selector produced an unknown instruction descriptor")); - if (!DiscoveredInstructions.insert(Candidate->stableID()).second) - continue; - Worklist.push_back(Candidate); - } + auto Discovered = DiscoverNewInstructions(); + if (!Discovered) + return std::unexpected(Discovered.error()); } for (MachineInstr *Instruction : layoutInstructions(Function)) { const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode()); diff --git a/backend/lib/TableGen/Parser.cpp b/backend/lib/TableGen/Parser.cpp index 248e831..680104e 100644 --- a/backend/lib/TableGen/Parser.cpp +++ b/backend/lib/TableGen/Parser.cpp @@ -1182,6 +1182,28 @@ private: } [[nodiscard]] bool + parseSelectionFloatingPredicate(const yaml::Node &Node, + std::optional<SourceSelectionFloatingPredicate> &Result) { + if (!requireMapping(&Node, "selection floating predicate") || + !checkKeys(Node, {"index", "value"}, "selection floating predicate")) { + return false; + } + const yaml::Node *IndexNode = requiredNode(Node, "index", "selection floating predicate"); + const auto Value = requiredScalar(Node, "value", "selection floating predicate"); + if (IndexNode == nullptr || !Value) + return false; + const auto Index = unsignedInteger32(*IndexNode, "selection floating predicate index"); + if (!Index) + return false; + Result = SourceSelectionFloatingPredicate{ + .OperandIndex = *Index, + .Predicate = *Value, + .Range = Node.range(), + }; + return true; + } + + [[nodiscard]] bool parseSelectionTargetPredicates(const yaml::Node &Node, std::vector<SourceSelectionTargetPredicate> &Result) { if (!requireSequence(&Node, "selection target predicates")) @@ -1552,9 +1574,9 @@ private: [[nodiscard]] bool parseSelectionRuleMatch(const yaml::Node &Node, SourceSelectionRule &Result) { if (!requireMapping(&Node, "selection rule match") || !checkKeys(Node, - {"opcode", "result_type", "integer_predicate", "target_predicates", - "operand_types", "operand_banks", "operand_classes", "operand_immediates", - "operand_def_constants", "def_use"}, + {"opcode", "result_type", "integer_predicate", "floating_predicate", + "target_predicates", "operand_types", "operand_banks", "operand_classes", + "operand_immediates", "operand_def_constants", "def_use"}, "selection rule match")) { return false; } @@ -1572,6 +1594,10 @@ private: if (!parseSelectionIntegerPredicate(*Predicate, Result.IntegerPredicate)) return false; } + if (const yaml::Node *Predicate = optionalNode(Node, "floating_predicate")) { + if (!parseSelectionFloatingPredicate(*Predicate, Result.FloatingPredicate)) + return false; + } if (const yaml::Node *TargetPredicates = optionalNode(Node, "target_predicates")) { if (!parseSelectionTargetPredicates(*TargetPredicates, Result.TargetPredicates)) return false; diff --git a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp index dd63743..db7ef45 100644 --- a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp +++ b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp @@ -66,6 +66,76 @@ operandType(const MachineFunction &Function, const MachineInstr &Instruction, st (Type.scalarBitWidth() == 32 || Type.scalarBitWidth() == 64); } +[[nodiscard]] Expected<InstructionOpcode> floatingCompareOpcode(FloatingPredicate Predicate, + std::uint32_t BitWidth) { + if (BitWidth == 32) { + switch (Predicate) { + case FloatingPredicate::OrderedEqual: + return InstructionOpcode::FEQ_S; + case FloatingPredicate::OrderedLessThan: + return InstructionOpcode::FLT_S; + case FloatingPredicate::OrderedLessEqual: + return InstructionOpcode::FLE_S; + default: + break; + } + } else if (BitWidth == 64) { + switch (Predicate) { + case FloatingPredicate::OrderedEqual: + return InstructionOpcode::FEQ_D; + case FloatingPredicate::OrderedLessThan: + return InstructionOpcode::FLT_D; + case FloatingPredicate::OrderedLessEqual: + return InstructionOpcode::FLE_D; + default: + break; + } + } + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V floating comparison predicate is not directly supported"); +} + +// Convert a floating predicate by swapping or inverting it to one supported by +// a scalar RISC-V comparison instruction. +[[nodiscard]] bool legalizeFloatingPredicate(Register &Left, Register &Right, + FloatingPredicate &Predicate, + bool &NeedsInvert) noexcept { + switch (Predicate) { + case FloatingPredicate::OrderedGreaterThan: + Predicate = FloatingPredicate::OrderedLessThan; + std::swap(Left, Right); + return true; + case FloatingPredicate::OrderedGreaterEqual: + Predicate = FloatingPredicate::OrderedLessEqual; + std::swap(Left, Right); + return true; + case FloatingPredicate::UnorderedNotEqual: + Predicate = FloatingPredicate::OrderedEqual; + NeedsInvert = true; + return true; + case FloatingPredicate::UnorderedGreaterThan: + Predicate = FloatingPredicate::OrderedLessEqual; + NeedsInvert = true; + return true; + case FloatingPredicate::UnorderedGreaterEqual: + Predicate = FloatingPredicate::OrderedLessThan; + NeedsInvert = true; + return true; + case FloatingPredicate::UnorderedLessThan: + Predicate = FloatingPredicate::OrderedLessEqual; + NeedsInvert = true; + std::swap(Left, Right); + return true; + case FloatingPredicate::UnorderedLessEqual: + Predicate = FloatingPredicate::OrderedLessThan; + NeedsInvert = true; + std::swap(Left, Right); + return true; + default: + return false; + } +} + [[nodiscard]] bool isSupportedIntegerType(LowLevelType Type) noexcept { return (Type.isCategoryNeutralScalar() && Type.scalarBitWidth() != 0 && Type.scalarBitWidth() <= 64) || @@ -955,168 +1025,158 @@ private: Destination == nullptr || Left == nullptr || Right == nullptr || !FloatType || !ResultType) return makeUnexpected(ErrorCode::VerificationError, "RISC-V floating comparison has invalid operands"); - const FloatingPredicate Condition = *Predicate->floatingPredicate(); + FloatingPredicate Condition = *Predicate->floatingPredicate(); const Register DestinationRegister = *Destination; - const Register LeftRegister = *Left; - const Register RightRegister = *Right; - const InstructionOpcode EqualOpcode = - FloatType->scalarBitWidth() == 32 ? InstructionOpcode::FEQ_S : InstructionOpcode::FEQ_D; - const InstructionOpcode LessOpcode = - FloatType->scalarBitWidth() == 32 ? InstructionOpcode::FLT_S : InstructionOpcode::FLT_D; - const InstructionOpcode LessEqualOpcode = - FloatType->scalarBitWidth() == 32 ? InstructionOpcode::FLE_S : InstructionOpcode::FLE_D; - - auto ErasePredicate = Instruction.eraseOperand(1); - if (!ErasePredicate) - return std::unexpected(ErasePredicate.error()); - const auto SetBase = [&Instruction](InstructionOpcode OpcodeValue, Register Def, - Register NewLeft, Register NewRight) -> Expected<void> { - auto SetDestination = Instruction.operandAt(0)->setRegister(Def); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto SetLeft = Instruction.operandAt(1)->setRegister(NewLeft); - if (!SetLeft) - return std::unexpected(SetLeft.error()); - auto SetRight = Instruction.operandAt(2)->setRegister(NewRight); - if (!SetRight) - return std::unexpected(SetRight.error()); - return Instruction.setOpcode(instructionOpcode(OpcodeValue)); + Register LeftRegister = *Left; + Register RightRegister = *Right; + MachineIRBuilder Builder(Function, Target); + + const auto BuildBinary = [&](InstructionOpcode OpcodeValue, Register Def, Register LeftValue, + Register RightValue) -> Expected<MachineInstr *> { + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto Registers = constrainBuilderRegisters(Function, Target, {Def, LeftValue, RightValue}); + if (!Registers) + return std::unexpected(Registers.error()); + auto Pending = Builder.buildInstr(instructionOpcode(OpcodeValue)); + if (!Pending) + return std::unexpected(Pending.error()); + auto DefResult = Pending->addDef(Def); + if (!DefResult) + return std::unexpected(DefResult.error()); + auto LeftResult = Pending->addUse(LeftValue); + if (!LeftResult) + return std::unexpected(LeftResult.error()); + auto RightResult = Pending->addUse(RightValue); + if (!RightResult) + return std::unexpected(RightResult.error()); + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + auto Constraint = constrainSelectedInstruction(**Built, Function, Target); + if (!Constraint) + return std::unexpected(Constraint.error()); + return *Built; }; - const auto BuildInverted = [&](InstructionOpcode BaseOpcode, Register BaseLeft, - Register BaseRight) -> Expected<void> { - auto Temporary = createTemporary(Function, *ResultType, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto Base = SetBase(BaseOpcode, *Temporary, BaseLeft, BaseRight); - if (!Base) - return std::unexpected(Base.error()); - MachineIRBuilder Builder(Function, Target); - auto Invert = buildImmediateAfter(Builder, Instruction, InstructionOpcode::XORI, - DestinationRegister, *Temporary, 1); - if (!Invert) - return std::unexpected(Invert.error()); - return {}; + const auto BuildImmediate = [&](InstructionOpcode OpcodeValue, Register Def, Register Source, + std::int64_t Immediate) -> Expected<MachineInstr *> { + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto Registers = constrainBuilderRegisters(Function, Target, {Def, Source}); + if (!Registers) + return std::unexpected(Registers.error()); + auto Pending = Builder.buildInstr(instructionOpcode(OpcodeValue)); + if (!Pending) + return std::unexpected(Pending.error()); + auto DefResult = Pending->addDef(Def); + if (!DefResult) + return std::unexpected(DefResult.error()); + auto SourceResult = Pending->addUse(Source); + if (!SourceResult) + return std::unexpected(SourceResult.error()); + auto ImmediateResult = Pending->addImmediate(Immediate); + if (!ImmediateResult) + return std::unexpected(ImmediateResult.error()); + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + auto Constraint = constrainSelectedInstruction(**Built, Function, Target); + if (!Constraint) + return std::unexpected(Constraint.error()); + return *Built; }; - const auto BuildSelfAnd = [&](bool Invert) -> Expected<void> { - auto First = createTemporary(Function, *ResultType, Target); - auto Second = createTemporary(Function, *ResultType, Target); - if (!First || !Second) - return std::unexpected((!First ? First.error() : Second.error())); - auto Base = SetBase(EqualOpcode, *First, LeftRegister, LeftRegister); - if (!Base) - return std::unexpected(Base.error()); - MachineIRBuilder Builder(Function, Target); - auto SecondInstruction = buildBinaryAfter(Builder, Instruction, EqualOpcode, *Second, - RightRegister, RightRegister); - if (!SecondInstruction) - return std::unexpected(SecondInstruction.error()); - Register CombinedDestination = DestinationRegister; - if (Invert) { + + Register TemporaryResult = DestinationRegister; + bool NeedsInvert = false; + if (legalizeFloatingPredicate(LeftRegister, RightRegister, Condition, NeedsInvert)) { + if (NeedsInvert) { auto Temporary = createTemporary(Function, *ResultType, Target); if (!Temporary) return std::unexpected(Temporary.error()); - CombinedDestination = *Temporary; + TemporaryResult = *Temporary; } - auto Combined = buildBinaryAfter(Builder, **SecondInstruction, InstructionOpcode::AND, - CombinedDestination, *First, *Second); - if (!Combined) - return std::unexpected(Combined.error()); - if (!Invert) - return {}; - auto Inverted = buildImmediateAfter(Builder, **Combined, InstructionOpcode::XORI, - DestinationRegister, CombinedDestination, 1); - if (!Inverted) - return std::unexpected(Inverted.error()); - return {}; - }; - const auto BuildOrderedNotEqual = [&](bool Invert) -> Expected<void> { + auto OpcodeValue = floatingCompareOpcode(Condition, FloatType->scalarBitWidth()); + if (!OpcodeValue) + return std::unexpected(OpcodeValue.error()); + auto Comparison = BuildBinary(*OpcodeValue, TemporaryResult, LeftRegister, RightRegister); + if (!Comparison) + return std::unexpected(Comparison.error()); + } else if (Condition == FloatingPredicate::OrderedNotEqual || + Condition == FloatingPredicate::UnorderedEqual) { + NeedsInvert = Condition == FloatingPredicate::UnorderedEqual; auto First = createTemporary(Function, *ResultType, Target); auto Second = createTemporary(Function, *ResultType, Target); if (!First || !Second) return std::unexpected((!First ? First.error() : Second.error())); - auto Base = SetBase(LessOpcode, *First, LeftRegister, RightRegister); - if (!Base) - return std::unexpected(Base.error()); - MachineIRBuilder Builder(Function, Target); - auto SecondInstruction = - buildBinaryAfter(Builder, Instruction, LessOpcode, *Second, RightRegister, LeftRegister); - if (!SecondInstruction) - return std::unexpected(SecondInstruction.error()); - Register CombinedDestination = DestinationRegister; - if (Invert) { + auto LessOpcode = + floatingCompareOpcode(FloatingPredicate::OrderedLessThan, FloatType->scalarBitWidth()); + if (!LessOpcode) + return std::unexpected(LessOpcode.error()); + auto FirstComparison = BuildBinary(*LessOpcode, *First, LeftRegister, RightRegister); + if (!FirstComparison) + return std::unexpected(FirstComparison.error()); + auto SecondComparison = BuildBinary(*LessOpcode, *Second, RightRegister, LeftRegister); + if (!SecondComparison) + return std::unexpected(SecondComparison.error()); + if (NeedsInvert) { auto Temporary = createTemporary(Function, *ResultType, Target); if (!Temporary) return std::unexpected(Temporary.error()); - CombinedDestination = *Temporary; + TemporaryResult = *Temporary; } - auto Combined = buildBinaryAfter(Builder, **SecondInstruction, InstructionOpcode::OR, - CombinedDestination, *First, *Second); + auto Combined = BuildBinary(InstructionOpcode::OR, TemporaryResult, *First, *Second); if (!Combined) return std::unexpected(Combined.error()); - if (!Invert) - return {}; - auto Inverted = buildImmediateAfter(Builder, **Combined, InstructionOpcode::XORI, - DestinationRegister, CombinedDestination, 1); + } else if (Condition == FloatingPredicate::Ordered || + Condition == FloatingPredicate::Unordered) { + NeedsInvert = Condition == FloatingPredicate::Unordered; + auto EqualOpcode = + floatingCompareOpcode(FloatingPredicate::OrderedEqual, FloatType->scalarBitWidth()); + if (!EqualOpcode) + return std::unexpected(EqualOpcode.error()); + if (NeedsInvert) { + auto Temporary = createTemporary(Function, *ResultType, Target); + if (!Temporary) + return std::unexpected(Temporary.error()); + TemporaryResult = *Temporary; + } + if (LeftRegister == RightRegister) { + auto Comparison = BuildBinary(*EqualOpcode, TemporaryResult, LeftRegister, LeftRegister); + if (!Comparison) + return std::unexpected(Comparison.error()); + } else { + auto First = createTemporary(Function, *ResultType, Target); + auto Second = createTemporary(Function, *ResultType, Target); + if (!First || !Second) + return std::unexpected((!First ? First.error() : Second.error())); + auto FirstComparison = BuildBinary(*EqualOpcode, *First, LeftRegister, LeftRegister); + if (!FirstComparison) + return std::unexpected(FirstComparison.error()); + auto SecondComparison = BuildBinary(*EqualOpcode, *Second, RightRegister, RightRegister); + if (!SecondComparison) + return std::unexpected(SecondComparison.error()); + auto Combined = BuildBinary(InstructionOpcode::AND, TemporaryResult, *First, *Second); + if (!Combined) + return std::unexpected(Combined.error()); + } + } else { + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V floating comparison predicate has no LLVM selection"); + } + + if (NeedsInvert) { + auto Inverted = + BuildImmediate(InstructionOpcode::XORI, DestinationRegister, TemporaryResult, 1); if (!Inverted) return std::unexpected(Inverted.error()); - return {}; - }; - - switch (Condition) { - case FloatingPredicate::AlwaysFalse: - case FloatingPredicate::AlwaysTrue: { - auto EraseRight = Instruction.eraseOperand(2); - if (!EraseRight) - return std::unexpected(EraseRight.error()); - auto EraseLeft = Instruction.eraseOperand(1); - if (!EraseLeft) - return std::unexpected(EraseLeft.error()); - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto InsertZero = insertPhysicalRegister(Instruction, 1, *Zero, RegisterOperandFlag::Use); - if (!InsertZero) - return std::unexpected(InsertZero.error()); - auto Immediate = - appendImmediate(Instruction, Condition == FloatingPredicate::AlwaysTrue ? 1 : 0); - if (!Immediate) - return std::unexpected(Immediate.error()); - return Instruction.setOpcode(instructionOpcode(InstructionOpcode::ADDI)); - } - case FloatingPredicate::OrderedEqual: - return SetBase(EqualOpcode, DestinationRegister, LeftRegister, RightRegister); - case FloatingPredicate::OrderedGreaterThan: - return SetBase(LessOpcode, DestinationRegister, RightRegister, LeftRegister); - case FloatingPredicate::OrderedGreaterEqual: - return SetBase(LessEqualOpcode, DestinationRegister, RightRegister, LeftRegister); - case FloatingPredicate::OrderedLessThan: - return SetBase(LessOpcode, DestinationRegister, LeftRegister, RightRegister); - case FloatingPredicate::OrderedLessEqual: - return SetBase(LessEqualOpcode, DestinationRegister, LeftRegister, RightRegister); - case FloatingPredicate::OrderedNotEqual: - return BuildOrderedNotEqual(false); - case FloatingPredicate::Ordered: - return BuildSelfAnd(false); - case FloatingPredicate::Unordered: - return BuildSelfAnd(true); - case FloatingPredicate::UnorderedEqual: - return BuildOrderedNotEqual(true); - case FloatingPredicate::UnorderedGreaterThan: - return BuildInverted(LessEqualOpcode, LeftRegister, RightRegister); - case FloatingPredicate::UnorderedGreaterEqual: - return BuildInverted(LessOpcode, LeftRegister, RightRegister); - case FloatingPredicate::UnorderedLessThan: - return BuildInverted(LessEqualOpcode, RightRegister, LeftRegister); - case FloatingPredicate::UnorderedLessEqual: - return BuildInverted(LessOpcode, RightRegister, LeftRegister); - case FloatingPredicate::UnorderedNotEqual: - return BuildInverted(EqualOpcode, LeftRegister, RightRegister); - case FloatingPredicate::Count: - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V floating comparison predicate is invalid"); } - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V floating comparison predicate is invalid"); + MachineBasicBlock *Block = Instruction.parent(); + if (Block == nullptr) + return makeUnexpected(ErrorCode::InternalError, + "RISC-V floating comparison is detached from its block"); + return Block->removeInstruction(Instruction); } [[nodiscard]] Expected<void> selectFence(MachineInstr &Instruction) const { diff --git a/backend/lib/Target/RISCV/RISCVGISelFloating.rccdesc b/backend/lib/Target/RISCV/RISCVGISelFloating.rccdesc index 18046b5..a5d9107 100644 --- a/backend/lib/Target/RISCV/RISCVGISelFloating.rccdesc +++ b/backend/lib/Target/RISCV/RISCVGISelFloating.rccdesc @@ -1,5 +1,119 @@ selection_rules: - + id: select_g_fcmp_oeq_s32 + priority: 100 + match: + opcode: G_FCMP + result_type: { kind: scalar, bits: 64 } + floating_predicate: { index: 1, value: oeq } + operand_types: + - { index: 2, kind: scalar, bits: 32 } + - { index: 3, kind: scalar, bits: 32 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: FPR } + - { index: 3, bank: FPR } + apply: + instructions: + - opcode: FEQ_S + root: true + operands: [{ operand: 0 }, { operand: 2 }, { operand: 3 }] + - + id: select_g_fcmp_olt_s32 + priority: 100 + match: + opcode: G_FCMP + result_type: { kind: scalar, bits: 64 } + floating_predicate: { index: 1, value: olt } + operand_types: + - { index: 2, kind: scalar, bits: 32 } + - { index: 3, kind: scalar, bits: 32 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: FPR } + - { index: 3, bank: FPR } + apply: + instructions: + - opcode: FLT_S + root: true + operands: [{ operand: 0 }, { operand: 2 }, { operand: 3 }] + - + id: select_g_fcmp_ole_s32 + priority: 100 + match: + opcode: G_FCMP + result_type: { kind: scalar, bits: 64 } + floating_predicate: { index: 1, value: ole } + operand_types: + - { index: 2, kind: scalar, bits: 32 } + - { index: 3, kind: scalar, bits: 32 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: FPR } + - { index: 3, bank: FPR } + apply: + instructions: + - opcode: FLE_S + root: true + operands: [{ operand: 0 }, { operand: 2 }, { operand: 3 }] + - + id: select_g_fcmp_oeq_s64 + priority: 100 + match: + opcode: G_FCMP + result_type: { kind: scalar, bits: 64 } + floating_predicate: { index: 1, value: oeq } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + - { index: 3, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: FPR } + - { index: 3, bank: FPR } + apply: + instructions: + - opcode: FEQ_D + root: true + operands: [{ operand: 0 }, { operand: 2 }, { operand: 3 }] + - + id: select_g_fcmp_olt_s64 + priority: 100 + match: + opcode: G_FCMP + result_type: { kind: scalar, bits: 64 } + floating_predicate: { index: 1, value: olt } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + - { index: 3, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: FPR } + - { index: 3, bank: FPR } + apply: + instructions: + - opcode: FLT_D + root: true + operands: [{ operand: 0 }, { operand: 2 }, { operand: 3 }] + - + id: select_g_fcmp_ole_s64 + priority: 100 + match: + opcode: G_FCMP + result_type: { kind: scalar, bits: 64 } + floating_predicate: { index: 1, value: ole } + operand_types: + - { index: 2, kind: scalar, bits: 64 } + - { index: 3, kind: scalar, bits: 64 } + operand_banks: + - { index: 0, bank: GPR } + - { index: 2, bank: FPR } + - { index: 3, bank: FPR } + apply: + instructions: + - opcode: FLE_D + root: true + operands: [{ operand: 0 }, { operand: 2 }, { operand: 3 }] + - id: select_g_fadd_s32 priority: 100 match: diff --git a/backend/utils/TableGen/Common/CodeGenSelection.cpp b/backend/utils/TableGen/Common/CodeGenSelection.cpp index df9bf7b..defe98b 100644 --- a/backend/utils/TableGen/Common/CodeGenSelection.cpp +++ b/backend/utils/TableGen/Common/CodeGenSelection.cpp @@ -79,6 +79,33 @@ findInstructionBySymbol(const NormalizedTargetModel &Model, std::string_view Sym return static_cast<std::uint32_t>(Position->second); } +[[nodiscard]] std::optional<std::uint32_t> +floatingPredicateValue(std::string_view Symbol) noexcept { + static constexpr std::array<std::pair<std::string_view, FloatingPredicate>, 16> Predicates = {{ + {"false", FloatingPredicate::AlwaysFalse}, + {"oeq", FloatingPredicate::OrderedEqual}, + {"ogt", FloatingPredicate::OrderedGreaterThan}, + {"oge", FloatingPredicate::OrderedGreaterEqual}, + {"olt", FloatingPredicate::OrderedLessThan}, + {"ole", FloatingPredicate::OrderedLessEqual}, + {"one", FloatingPredicate::OrderedNotEqual}, + {"ord", FloatingPredicate::Ordered}, + {"uno", FloatingPredicate::Unordered}, + {"ueq", FloatingPredicate::UnorderedEqual}, + {"ugt", FloatingPredicate::UnorderedGreaterThan}, + {"uge", FloatingPredicate::UnorderedGreaterEqual}, + {"ult", FloatingPredicate::UnorderedLessThan}, + {"ule", FloatingPredicate::UnorderedLessEqual}, + {"une", FloatingPredicate::UnorderedNotEqual}, + {"true", FloatingPredicate::AlwaysTrue}, + }}; + const auto *const Position = + std::ranges::find(Predicates, Symbol, &decltype(Predicates)::value_type::first); + if (Position == Predicates.end()) + return std::nullopt; + return static_cast<std::uint32_t>(Position->second); +} + [[nodiscard]] const NormalizedRegister *findRegisterBySymbol(const NormalizedTargetModel &Model, std::string_view Symbol) { const auto Position = std::ranges::find(Model.Registers, Symbol, &NormalizedRegister::Symbol); @@ -243,6 +270,33 @@ bool CodeGenTarget::normalizeSelectionRules(NormalizedTargetModel &Result) { ++Normalized.Specificity; } + if (Rule.IntegerPredicate && Rule.FloatingPredicate) { + fail(Rule.Range, "selection rule cannot require both integer and floating predicates"); + return false; + } + if (Rule.FloatingPredicate) { + const auto Predicate = floatingPredicateValue(Rule.FloatingPredicate->Predicate); + if (!Predicate) { + fail(Rule.FloatingPredicate->Range, + "selection floating predicate is not a recognized floating predicate"); + return false; + } + if (MatchInstruction != nullptr && + (Rule.FloatingPredicate->OperandIndex >= MatchInstruction->Operands.size() || + !std::ranges::contains( + MatchInstruction->Operands[Rule.FloatingPredicate->OperandIndex].Kinds, + "Predicate"))) { + fail(Rule.FloatingPredicate->Range, + "selection floating predicate requires a predicate operand"); + return false; + } + Normalized.FloatingPredicate = { + .OperandIndex = Rule.FloatingPredicate->OperandIndex, + .Predicate = *Predicate, + }; + ++Normalized.Specificity; + } + std::unordered_set<std::string> TargetPredicateSymbols; for (const SourceSelectionTargetPredicate &Predicate : Rule.TargetPredicates) { if (!isIdentifier(Predicate.Symbol) || diff --git a/backend/utils/TableGen/Common/CodeGenTarget.h b/backend/utils/TableGen/Common/CodeGenTarget.h index 9a7c0a2..020bb88 100644 --- a/backend/utils/TableGen/Common/CodeGenTarget.h +++ b/backend/utils/TableGen/Common/CodeGenTarget.h @@ -195,6 +195,11 @@ struct NormalizedSelectionIntegerPredicate final { std::uint32_t Predicate = 0; }; +struct NormalizedSelectionFloatingPredicate final { + std::uint32_t OperandIndex = 0; + std::uint32_t Predicate = 0; +}; + struct NormalizedSelectionConstantRange final { std::int64_t Minimum = 0; std::int64_t Maximum = 0; @@ -266,6 +271,7 @@ struct NormalizedSelectionRule final { std::uint32_t MatchOpcode = 0; std::optional<NormalizedSelectionTypePredicate> ResultType; std::optional<NormalizedSelectionIntegerPredicate> IntegerPredicate; + std::optional<NormalizedSelectionFloatingPredicate> FloatingPredicate; std::vector<std::string> TargetPredicates; std::vector<NormalizedSelectionOperandTypePredicate> OperandTypes; std::vector<NormalizedSelectionOperandBankPredicate> OperandBanks; diff --git a/backend/utils/TableGen/GlobalISelEmitter.cpp b/backend/utils/TableGen/GlobalISelEmitter.cpp index d73d691..7390bdf 100644 --- a/backend/utils/TableGen/GlobalISelEmitter.cpp +++ b/backend/utils/TableGen/GlobalISelEmitter.cpp @@ -42,10 +42,11 @@ void emitTypePredicate(std::ostream &Output, std::uint32_t OperandIndex, [[nodiscard]] std::size_t instructionCount(const NormalizedSelectionRule &Rule) noexcept { std::size_t Count = 2U + (Rule.ResultType ? 1U : 0U) + (Rule.IntegerPredicate ? 1U : 0U) + - Rule.TargetPredicates.size() + Rule.OperandTypes.size() + - Rule.OperandBanks.size() + Rule.OperandClasses.size() + - Rule.OperandImmediates.size() + Rule.OperandDefConstants.size() + - Rule.Temporaries.size() + Rule.EraseOperandDefinitions.size(); + (Rule.FloatingPredicate ? 1U : 0U) + Rule.TargetPredicates.size() + + Rule.OperandTypes.size() + Rule.OperandBanks.size() + + Rule.OperandClasses.size() + Rule.OperandImmediates.size() + + Rule.OperandDefConstants.size() + Rule.Temporaries.size() + + Rule.EraseOperandDefinitions.size(); for (const NormalizedSelectionDefUsePredicate &Predicate : Rule.DefUsePredicates) Count += 1U + static_cast<std::size_t>(Predicate.IntegerPredicate.has_value()) + Predicate.Captures.size() + Predicate.OperandDefConstants.size(); @@ -115,6 +116,9 @@ std::string emitGlobalISel(const NormalizedTargetModel &Model, std::string_view if (Rule.IntegerPredicate) emitInstruction(Output, "CheckIntegerPredicate", Rule.IntegerPredicate->OperandIndex, Rule.IntegerPredicate->Predicate); + if (Rule.FloatingPredicate) + emitInstruction(Output, "CheckFloatingPredicate", Rule.FloatingPredicate->OperandIndex, + Rule.FloatingPredicate->Predicate); for (const std::string &Predicate : Rule.TargetPredicates) emitInstruction(Output, "CheckTargetPredicate", 0, 0, "0", 0, Predicate); for (const NormalizedSelectionOperandTypePredicate &Predicate : Rule.OperandTypes) |
