diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-11 17:38:11 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-11 17:38:11 +0800 |
| commit | 4fe5ee649927d54d6fb74904d7b97ad0f63fd587 (patch) | |
| tree | 75f85f5e6cae3a871fe42d025dca04eeb7c31cfd /backend/lib/Target/RISCV | |
| parent | 6a7b083f322f4ac47e58cbf9d4d5fd4d1f6fecd0 (diff) | |
| download | rcc-project-4fe5ee649927d54d6fb74904d7b97ad0f63fd587.tar.gz rcc-project-4fe5ee649927d54d6fb74904d7b97ad0f63fd587.tar.xz | |
[RISCV] Legalize fixed-baseline RV64 MachineIR
Diffstat (limited to 'backend/lib/Target/RISCV')
| -rw-r--r-- | backend/lib/Target/RISCV/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCV.rccdesc | 27 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVBranchInfo.cpp | 15 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVCodeGen.cpp | 2455 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVFunctionInfo.cpp | 137 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVImmediateMaterialization.cpp | 148 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVImmediateMaterialization.h | 22 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp | 40 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVTarget.cpp | 4 |
9 files changed, 2824 insertions, 32 deletions
diff --git a/backend/lib/Target/RISCV/CMakeLists.txt b/backend/lib/Target/RISCV/CMakeLists.txt index 156dfd6..eae426c 100644 --- a/backend/lib/Target/RISCV/CMakeLists.txt +++ b/backend/lib/Target/RISCV/CMakeLists.txt @@ -52,6 +52,8 @@ add_library( RISCVCallLowering.cpp RISCVAssemblyPrinter.cpp RISCVFrameLowering.cpp + RISCVFunctionInfo.cpp + RISCVImmediateMaterialization.cpp RISCVInstPrinter.cpp RISCVAsmBackend.cpp RISCVMCCodeEmitter.cpp @@ -118,6 +120,9 @@ rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/RISCVCodeGen.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVCallLowering.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVFrameLowering.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVFunctionInfo.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVImmediateMaterialization.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVImmediateMaterialization.h" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVInstPrinter.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVAsmBackend.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVMCCodeEmitter.cpp" @@ -138,6 +143,7 @@ rcc_backend_register_format_files( "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVInstPrinter.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVAsmBackend.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVFixupKinds.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVFunctionInfo.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVMCCodeEmitter.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVObjectFile.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVPipeline.h" @@ -152,6 +158,8 @@ rcc_backend_register_tidy_files( "${CMAKE_CURRENT_SOURCE_DIR}/RISCVCodeGen.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVCallLowering.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVFrameLowering.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVFunctionInfo.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVImmediateMaterialization.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVInstPrinter.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVAsmBackend.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVMCCodeEmitter.cpp" diff --git a/backend/lib/Target/RISCV/RISCV.rccdesc b/backend/lib/Target/RISCV/RISCV.rccdesc index 237e294..f8e22d4 100644 --- a/backend/lib/Target/RISCV/RISCV.rccdesc +++ b/backend/lib/Target/RISCV/RISCV.rccdesc @@ -143,8 +143,8 @@ registers: - { id: 64, symbol: F31, name: f31, encoding: 31, dwarf_register: 63, units: [{ unit: U_F31, lanes: 1 }], aliases: [ft11] } register_banks: - - { id: 1, symbol: GPR, name: GPR, maximum_size_bits: 64 } - - { id: 2, symbol: FPR, name: FPR, maximum_size_bits: 64 } + - { id: 1, symbol: GPR, name: GPRB, maximum_size_bits: 64 } + - { id: 2, symbol: FPR, name: FPRB, maximum_size_bits: 64 } register_classes: - @@ -3277,6 +3277,18 @@ instructions: assembly_format: RD_RS2_PAREN_RS1 requires_memory_operands: true - + id: 1229 + symbol: G_SLLW + name: G_SLLW + operands: + - { kinds: [register], role: def } + - { kinds: [register], role: use } + - { kinds: [register], role: use } + num_explicit_defs: 1 + flags: [generic, pseudo, allowed_pre_legalizer] + owner: instruction_select + deadline: selected + - id: 1216 symbol: PseudoCC_COPY name: PseudoCC_COPY @@ -3386,7 +3398,6 @@ instructions: name: PseudoRET operands: [] num_explicit_defs: 0 - implicit_uses: [{ register: X1 }] allow_extra_implicit_operands: true flags: [pseudo, produces_code, allowed_pre_legalizer, terminator, return, barrier] owner: post_ra_expansion @@ -3443,3 +3454,13 @@ instructions: scheduling_class: BRANCH encoding_category: I assembly_format: RD_RS1_IMM + - + id: 1228 + symbol: PseudoBR + name: PseudoBR + operands: + - { kinds: [basic_block], name: TARGET } + num_explicit_defs: 0 + flags: [pseudo, produces_code, allowed_pre_legalizer, terminator, branch, barrier] + owner: post_ra_expansion + deadline: post_ra_finalized diff --git a/backend/lib/Target/RISCV/RISCVBranchInfo.cpp b/backend/lib/Target/RISCV/RISCVBranchInfo.cpp index 18812d8..a00ed67 100644 --- a/backend/lib/Target/RISCV/RISCVBranchInfo.cpp +++ b/backend/lib/Target/RISCV/RISCVBranchInfo.cpp @@ -75,15 +75,19 @@ constexpr std::uint32_t PCRelativeLowFlag = 4; } [[nodiscard]] bool isUnconditionalBranch(const MachineInstr &Instruction) noexcept { - if (Instruction.opcode() == targetOpcode(InstructionOpcode::C_J)) + if (Instruction.opcode() == targetOpcode(InstructionOpcode::C_J) || + Instruction.opcode() == targetOpcode(InstructionOpcode::PseudoBR)) { return true; + } return Instruction.opcode() == targetOpcode(InstructionOpcode::JAL) && isZeroRegister(Instruction.operandAt(0)); } [[nodiscard]] std::size_t branchTargetIndex(Opcode Value) noexcept { - if (Value == targetOpcode(InstructionOpcode::C_J)) + if (Value == targetOpcode(InstructionOpcode::C_J) || + Value == targetOpcode(InstructionOpcode::PseudoBR)) { return 0; + } if (Value == targetOpcode(InstructionOpcode::C_BEQZ) || Value == targetOpcode(InstructionOpcode::C_BNEZ)) { return 1; @@ -96,7 +100,8 @@ constexpr std::uint32_t PCRelativeLowFlag = 4; [[nodiscard]] Expected<MachineBasicBlock *> directTarget(MachineInstr &Instruction) { if (!isConditionalBranch(Instruction.opcode()) && Instruction.opcode() != targetOpcode(InstructionOpcode::JAL) && - Instruction.opcode() != targetOpcode(InstructionOpcode::C_J)) { + Instruction.opcode() != targetOpcode(InstructionOpcode::C_J) && + Instruction.opcode() != targetOpcode(InstructionOpcode::PseudoBR)) { return static_cast<MachineBasicBlock *>(nullptr); } MachineOperand *Operand = Instruction.operandAt(branchTargetIndex(Instruction.opcode())); @@ -697,8 +702,10 @@ public: return Offset >= -2048 && Offset <= 2046; if (isConditionalBranch(Value)) return Offset >= -4096 && Offset <= 4094; - if (Value == targetOpcode(InstructionOpcode::JAL)) + if (Value == targetOpcode(InstructionOpcode::JAL) || + Value == targetOpcode(InstructionOpcode::PseudoBR)) { return Offset >= -1048576 && Offset <= 1048574; + } return true; } diff --git a/backend/lib/Target/RISCV/RISCVCodeGen.cpp b/backend/lib/Target/RISCV/RISCVCodeGen.cpp index 642982a..bfbf9a6 100644 --- a/backend/lib/Target/RISCV/RISCVCodeGen.cpp +++ b/backend/lib/Target/RISCV/RISCVCodeGen.cpp @@ -2,6 +2,8 @@ #include "rcc/Target/RISCV/RISCVCodeGen.h" +#include "RISCVImmediateMaterialization.h" + #include "rcc/CodeGen/GenericOpcodes.h" #include "rcc/CodeGen/LowLevelType.h" #include "rcc/CodeGen/MachineBasicBlock.h" @@ -64,39 +66,655 @@ namespace { } [[nodiscard]] bool isSupportedIntegerType(LLT Type) noexcept { - return (Type.isInteger() && Type.scalarBitWidth() != 0 && Type.scalarBitWidth() <= 64) || + return ((Type.isInteger() || Type.isAnyScalar()) && Type.scalarBitWidth() != 0 && + Type.scalarBitWidth() <= 64) || (Type.isPointer() && Type.scalarBitWidth() == 64); } [[nodiscard]] bool isSupportedIntegerScalar(LLT Type) noexcept { - return Type.isInteger() && Type.scalarBitWidth() != 0 && Type.scalarBitWidth() <= 64; + return (Type.isInteger() || Type.isAnyScalar()) && Type.scalarBitWidth() != 0 && + Type.scalarBitWidth() <= 64; } [[nodiscard]] bool isNativeIntegerScalar(LLT Type) noexcept { - return Type.isInteger() && (Type.scalarBitWidth() == 32 || Type.scalarBitWidth() == 64); + return (Type.isInteger() || Type.isAnyScalar()) && + (Type.scalarBitWidth() == 32 || Type.scalarBitWidth() == 64); } [[nodiscard]] bool isScalarGPRType(LLT Type) noexcept { - return (Type.isInteger() && Type.scalarBitWidth() != 0 && Type.scalarBitWidth() <= 64) || + return ((Type.isInteger() || Type.isAnyScalar()) && Type.scalarBitWidth() != 0 && + Type.scalarBitWidth() <= 64) || (Type.isPointer() && Type.scalarBitWidth() == 64); } [[nodiscard]] bool isI1Type(LLT Type) noexcept { - return Type.isInteger() && Type.scalarBitWidth() == 1; + return (Type.isInteger() || Type.isAnyScalar()) && Type.scalarBitWidth() == 1; } [[nodiscard]] bool isScalarIntegerExtension(LLT Destination, LLT Source) noexcept { - return Destination.isInteger() && Source.isInteger() && Destination.scalarBitWidth() != 0 && + return (Destination.isInteger() || Destination.isAnyScalar()) && + (Source.isInteger() || Source.isAnyScalar()) && Destination.scalarBitWidth() != 0 && Source.scalarBitWidth() != 0 && Source.scalarBitWidth() < Destination.scalarBitWidth() && Destination.scalarBitWidth() <= 64; } +[[nodiscard]] Expected<IntegerImmediate> +extendIntegerImmediate(const IntegerImmediate &Value, std::uint32_t DestinationWidth, + bool SignExtend) { + if (DestinationWidth == 0 || DestinationWidth > 64) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V constant extension requires a destination no wider than 64 bits"); + + std::uint64_t Raw = Value.words().empty() ? 0 : Value.words().front(); + const std::uint32_t SourceWidth = Value.bitWidth(); + if (SignExtend && Value.isNegative() && SourceWidth < 64) + Raw |= ~std::uint64_t{0} << SourceWidth; + if (DestinationWidth < 64) + Raw &= (std::uint64_t{1} << DestinationWidth) - 1U; + return IntegerImmediate::create(DestinationWidth, std::span<const std::uint64_t>(&Raw, 1)); +} + +[[nodiscard]] Expected<void> foldConstantExtension(MachineInstr &Instruction, + MachineFunction &Function, + GenericOpcode Generic) { + const MachineOperand *SourceOperand = Instruction.operandAt(1); + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + if (Source == nullptr || Destination == nullptr || !Source->isVirtual() || + !Destination->isVirtual()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V constant extension requires virtual-register operands"); + } + const MachineOperand *SourceDefinition = Function.registerInfo().getUniqueVRegDef(*Source); + const MachineInstr *SourceDef = SourceDefinition == nullptr ? nullptr : SourceDefinition->parent(); + if (SourceDef == nullptr || SourceDef->opcode() != opcode(GenericOpcode::G_CONSTANT)) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V constant extension requires a single constant definition"); + const MachineOperand *ConstantOperand = SourceDef->operandAt(1); + const auto *Integer = ConstantOperand == nullptr + ? nullptr + : ConstantOperand->getIf<IntegerImmediateOperand>(); + if (Integer == nullptr) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V constant extension requires an integer constant"); + const auto DestinationInfo = Function.registerInfo().virtualRegisterInfo(*Destination); + if (!DestinationInfo || !DestinationInfo->get().Type) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V constant extension destination has no type"); + const std::uint32_t DestinationWidth = DestinationInfo->get().Type->scalarBitWidth(); + const bool SignExtend = Generic != GenericOpcode::G_ZEXT; + auto Extended = extendIntegerImmediate(Integer->Value, DestinationWidth, SignExtend); + if (!Extended) + return std::unexpected(Extended.error()); + + auto SetOpcode = Instruction.setOpcode(opcode(GenericOpcode::G_CONSTANT)); + if (!SetOpcode) + return std::unexpected(SetOpcode.error()); + while (Instruction.operandCount() > 1) { + auto Erased = Instruction.eraseOperand(1); + if (!Erased) + return std::unexpected(Erased.error()); + } + auto Added = Instruction.addOperand(MachineOperand::integerImmediate(std::move(*Extended))); + if (!Added) + return std::unexpected(Added.error()); + + if (Function.registerInfo().useEmpty(*Source)) { + MachineInstr *Def = const_cast<MachineInstr *>(SourceDef); + MachineBasicBlock *Parent = Def->parent(); + if (Parent == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V constant extension source definition is detached"); + auto Removed = Parent->removeInstruction(*Def); + if (!Removed) + return std::unexpected(Removed.error()); + // Keep the source register record as a tombstone-free historical vreg until + // the legalizer worklist transaction has completed. + } + return {}; +} + +struct WideConstantUnmergePlan final { + MachineInstr *Extension = nullptr; + MachineInstr *Unmerge = nullptr; + std::vector<Register> Outputs; + bool SignExtend = false; +}; + +[[nodiscard]] MachineInstr *singleRegisterUse(const MachineFunction &Function, Register Reg) { + MachineInstr *UseInstruction = nullptr; + auto &RegisterInfo = const_cast<MachineRegisterInfo &>(Function.registerInfo()); + for (MachineOperand &Operand : RegisterInfo.useOperands(Reg)) { + if (Operand.parent() == nullptr) + continue; + if (UseInstruction != nullptr) + return nullptr; + UseInstruction = Operand.parent(); + } + return UseInstruction; +} + +[[nodiscard]] std::optional<WideConstantUnmergePlan> +wideConstantUnmergePlan(const MachineInstr &Constant, const MachineFunction &Function) { + const MachineOperand *ResultOperand = Constant.operandAt(0); + const Register *Result = ResultOperand == nullptr ? nullptr : ResultOperand->getIf<Register>(); + if (Result == nullptr || !Result->isVirtual()) + return std::nullopt; + + MachineInstr *FirstUse = singleRegisterUse(Function, *Result); + if (FirstUse == nullptr) + return std::nullopt; + MachineInstr *Extension = nullptr; + MachineInstr *Unmerge = FirstUse; + if (FirstUse->opcode() == opcode(GenericOpcode::G_ANYEXT) || + FirstUse->opcode() == opcode(GenericOpcode::G_SEXT) || + FirstUse->opcode() == opcode(GenericOpcode::G_ZEXT)) { + Extension = FirstUse; + const MachineOperand *ExtendedOperand = Extension->operandAt(0); + const Register *Extended = + ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); + if (Extended == nullptr || !Extended->isVirtual()) + return std::nullopt; + Unmerge = singleRegisterUse(Function, *Extended); + if (Unmerge == nullptr) + return std::nullopt; + } + if (Unmerge->opcode() != opcode(GenericOpcode::G_UNMERGE_VALUES) || + Unmerge->operandCount() < 3) + return std::nullopt; + + WideConstantUnmergePlan Plan; + Plan.Extension = Extension; + Plan.Unmerge = Unmerge; + Plan.SignExtend = Extension != nullptr && Extension->opcode() != opcode(GenericOpcode::G_ZEXT); + for (std::size_t Index = 0; Index + 1U < Unmerge->operandCount(); ++Index) { + const MachineOperand *Operand = Unmerge->operandAt(Index); + const Register *Reg = Operand == nullptr ? nullptr : Operand->getIf<Register>(); + if (Reg == nullptr || !Reg->isVirtual() || !Operand->isDef()) + return std::nullopt; + const auto Info = Function.registerInfo().virtualRegisterInfo(*Reg); + if (!Info || !Info->get().Type || Info->get().Type->scalarBitWidth() == 0 || + Info->get().Type->scalarBitWidth() > 64 || + (!Info->get().Type->isInteger() && !Info->get().Type->isAnyScalar())) + return std::nullopt; + Plan.Outputs.push_back(*Reg); + } + const MachineOperand *SourceOperand = Unmerge->operandAt(Unmerge->operandCount() - 1U); + const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); + const Register *ExpectedSource = Result; + if (Extension != nullptr) { + const MachineOperand *ExtendedOperand = Extension->operandAt(0); + ExpectedSource = ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); + } + if (Source == nullptr || ExpectedSource == nullptr || *Source != *ExpectedSource) + return std::nullopt; + return Plan; +} + +[[nodiscard]] Expected<IntegerImmediate> +wideConstantWord(const IntegerImmediate &Value, std::size_t WordIndex, std::uint32_t Width, + bool SignExtend) { + std::uint64_t Raw = 0; + if (WordIndex < Value.words().size()) + Raw = Value.words()[WordIndex]; + else if (SignExtend && Value.isNegative()) + Raw = std::numeric_limits<std::uint64_t>::max(); + const std::size_t WordBitOffset = WordIndex * 64U; + if (SignExtend && Value.isNegative() && WordBitOffset < Value.bitWidth()) { + const std::uint32_t Remaining = + Value.bitWidth() - static_cast<std::uint32_t>(WordBitOffset); + if (Remaining < 64) + Raw |= ~std::uint64_t{0} << Remaining; + } + if (Width < 64) + Raw &= (std::uint64_t{1} << Width) - 1U; + return IntegerImmediate::create(Width, std::span<const std::uint64_t>(&Raw, 1)); +} + +[[nodiscard]] Expected<void> foldWideConstant(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target) { + const auto Plan = wideConstantUnmergePlan(Instruction, Function); + if (!Plan) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide constant requires a G_UNMERGE_VALUES use"); + const MachineOperand *ImmediateOperand = Instruction.operandAt(1); + const auto *Integer = ImmediateOperand == nullptr + ? nullptr + : ImmediateOperand->getIf<IntegerImmediateOperand>(); + if (Integer == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide constant has no integer immediate"); + + std::vector<IntegerImmediate> Words; + Words.reserve(Plan->Outputs.size()); + for (std::size_t Index = 0; Index < Plan->Outputs.size(); ++Index) { + const auto Info = Function.registerInfo().virtualRegisterInfo(Plan->Outputs[Index]); + if (!Info || !Info->get().Type) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide constant output has no virtual-register type"); + auto Word = wideConstantWord(Integer->Value, Index, Info->get().Type->scalarBitWidth(), + Plan->SignExtend); + if (!Word) + return std::unexpected(Word.error()); + Words.push_back(std::move(*Word)); + } + + std::vector<std::size_t> Representatives(Plan->Outputs.size()); + for (std::size_t Index = 0; Index < Representatives.size(); ++Index) { + Representatives[Index] = Index; + for (std::size_t Previous = 0; Previous < Index; ++Previous) { + if (Words[Previous] == Words[Index]) { + Representatives[Index] = Previous; + break; + } + } + } + + const Register FirstOutput = Plan->Outputs.front(); + auto Retargeted = Instruction.operandAt(0)->setRegister(FirstOutput); + if (!Retargeted) + return std::unexpected(Retargeted.error()); + auto SetOpcode = Instruction.setOpcode(opcode(GenericOpcode::G_CONSTANT)); + if (!SetOpcode) + return std::unexpected(SetOpcode.error()); + while (Instruction.operandCount() > 1) { + auto Erased = Instruction.eraseOperand(1); + if (!Erased) + return std::unexpected(Erased.error()); + } + auto Added = Instruction.addOperand(MachineOperand::integerImmediate(std::move(Words.front()))); + if (!Added) + return std::unexpected(Added.error()); + + MachineBasicBlock *Block = Instruction.parent(); + if (Block == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide constant is detached during legalization"); + if (Plan->Unmerge != &Instruction) { + auto RemovedUnmerge = Plan->Unmerge->parent()->removeInstruction(*Plan->Unmerge); + if (!RemovedUnmerge) + return std::unexpected(RemovedUnmerge.error()); + } + if (Plan->Extension != nullptr) { + auto RemovedExtension = Plan->Extension->parent()->removeInstruction(*Plan->Extension); + if (!RemovedExtension) + return std::unexpected(RemovedExtension.error()); + } + + for (std::size_t Index = 1; Index < Plan->Outputs.size(); ++Index) { + if (Representatives[Index] == Index) + continue; + auto Replaced = Function.registerInfo().replaceRegWith( + Plan->Outputs[Index], Plan->Outputs[Representatives[Index]]); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + + MachineInstr *Last = &Instruction; + for (std::size_t Index = 1; Index < Plan->Outputs.size(); ++Index) { + if (Representatives[Index] != Index) + continue; + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointAfter(*Last); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_CONSTANT)); + if (!Pending) + return std::unexpected(Pending.error()); + auto Def = Pending->addDef(Plan->Outputs[Index]); + if (!Def) + return std::unexpected(Def.error()); + auto Value = Pending->addIntegerImmediate(std::move(Words[Index])); + if (!Value) + return std::unexpected(Value.error()); + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + Last = *Built; + } + return {}; +} + +[[nodiscard]] Expected<Register> +buildIntegerConstantBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, + std::uint32_t Width, std::uint64_t Value); + +[[nodiscard]] Expected<Register> +buildBinaryBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, + GenericOpcode Generic, LLT Type, Register Left, + Register Right); + +struct NarrowAnyExtBinaryPlan final { + MachineInstr *Binary = nullptr; + GenericOpcode Generic = GenericOpcode::Invalid; + std::array<Register, 2> WideInputs{}; + std::vector<MachineInstr *> Truncations; + std::vector<MachineInstr *> Constants; + std::uint32_t SourceWidth = 0; +}; + +[[nodiscard]] std::optional<NarrowAnyExtBinaryPlan> +narrowAnyExtBinaryPlan(const MachineInstr &Extension, const MachineFunction &Function) { + const MachineOperand *DestinationOperand = Extension.operandAt(0); + const MachineOperand *SourceOperand = Extension.operandAt(1); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); + if (Destination == nullptr || Source == nullptr || !Destination->isVirtual() || + !Source->isVirtual() || singleRegisterUse(Function, *Source) != &Extension) + return std::nullopt; + const auto DestinationInfo = Function.registerInfo().virtualRegisterInfo(*Destination); + const auto SourceInfo = Function.registerInfo().virtualRegisterInfo(*Source); + if (!DestinationInfo || !SourceInfo || !DestinationInfo->get().Type || !SourceInfo->get().Type) + return std::nullopt; + const LLT DestinationType = *DestinationInfo->get().Type; + const LLT SourceType = *SourceInfo->get().Type; + if (!isScalarIntegerExtension(DestinationType, SourceType) || + DestinationType.scalarBitWidth() != 64) + return std::nullopt; + + const MachineOperand *SourceDefinition = Function.registerInfo().getUniqueVRegDef(*Source); + MachineInstr *Binary = + SourceDefinition == nullptr ? nullptr : const_cast<MachineInstr *>(SourceDefinition->parent()); + if (Binary == nullptr || Binary->operandCount() != 3) + return std::nullopt; + const GenericOpcode Generic = static_cast<GenericOpcode>(Binary->opcode().value()); + if (Generic != GenericOpcode::G_ADD && Generic != GenericOpcode::G_SUB && + Generic != GenericOpcode::G_AND && Generic != GenericOpcode::G_OR && + Generic != GenericOpcode::G_XOR && Generic != GenericOpcode::G_SHL) + return std::nullopt; + + NarrowAnyExtBinaryPlan Plan; + Plan.Binary = Binary; + Plan.Generic = Generic; + Plan.SourceWidth = SourceType.scalarBitWidth(); + for (std::size_t Index = 0; Index != Plan.WideInputs.size(); ++Index) { + const MachineOperand *Operand = Binary->operandAt(Index + 1U); + const Register *Reg = Operand == nullptr ? nullptr : Operand->getIf<Register>(); + if (Reg == nullptr || !Reg->isVirtual()) + return std::nullopt; + const auto Info = Function.registerInfo().virtualRegisterInfo(*Reg); + if (!Info || !Info->get().Type) + return std::nullopt; + if (*Info->get().Type == DestinationType) { + Plan.WideInputs[Index] = *Reg; + continue; + } + if (*Info->get().Type != SourceType) + return std::nullopt; + + const MachineOperand *Definition = Function.registerInfo().getUniqueVRegDef(*Reg); + MachineInstr *Def = Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); + if (Def != nullptr && Def->opcode() == opcode(GenericOpcode::G_TRUNC) && + Def->operandCount() == 2) { + const MachineOperand *WideOperand = Def->operandAt(1); + const Register *Wide = WideOperand == nullptr ? nullptr : WideOperand->getIf<Register>(); + if (Wide == nullptr || !Wide->isVirtual()) + return std::nullopt; + const auto WideInfo = Function.registerInfo().virtualRegisterInfo(*Wide); + if (!WideInfo || !WideInfo->get().Type || *WideInfo->get().Type != DestinationType) + return std::nullopt; + Plan.WideInputs[Index] = *Wide; + Plan.Truncations.push_back(Def); + continue; + } + if (Def != nullptr && Def->opcode() == opcode(GenericOpcode::G_CONSTANT) && + singleRegisterUse(Function, *Reg) == Binary) { + Plan.WideInputs[Index] = *Reg; + Plan.Constants.push_back(Def); + continue; + } + return std::nullopt; + } + return Plan; +} + +[[nodiscard]] Expected<void> widenConstantToRV64(MachineInstr &Instruction, + MachineFunction &Function) { + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + const MachineOperand *ImmediateOperand = Instruction.operandAt(1); + const auto *Immediate = ImmediateOperand == nullptr + ? nullptr + : ImmediateOperand->getIf<IntegerImmediateOperand>(); + if (Destination == nullptr || !Destination->isVirtual() || Immediate == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V narrow constant legalization has invalid operands"); + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + auto SetType = Function.registerInfo().setType(*Destination, *Type); + if (!SetType) + return std::unexpected(SetType.error()); + auto Extended = extendIntegerImmediate(Immediate->Value, 64, true); + if (!Extended) + return std::unexpected(Extended.error()); + auto Erased = Instruction.eraseOperand(1); + if (!Erased) + return std::unexpected(Erased.error()); + auto Added = Instruction.addOperand(MachineOperand::integerImmediate(std::move(*Extended))); + if (!Added) + return std::unexpected(Added.error()); + return {}; +} + +[[nodiscard]] Expected<void> foldNarrowAnyExtBinary(MachineInstr &Extension, + MachineFunction &Function, + const TargetDescription &Target) { + const auto Plan = narrowAnyExtBinaryPlan(Extension, Function); + if (!Plan) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V narrow any-extension does not have a legal binary chain"); + for (MachineInstr *Constant : Plan->Constants) { + auto Widened = widenConstantToRV64(*Constant, Function); + if (!Widened) + return std::unexpected(Widened.error()); + } + for (std::size_t Index = 0; Index != Plan->WideInputs.size(); ++Index) { + auto Replaced = Plan->Binary->operandAt(Index + 1U)->setRegister(Plan->WideInputs[Index]); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + for (MachineInstr *Truncation : Plan->Truncations) { + auto Removed = Truncation->parent()->removeInstruction(*Truncation); + if (!Removed) + return std::unexpected(Removed.error()); + } + + if (Plan->Generic == GenericOpcode::G_SHL) { + Register Amount = Plan->WideInputs[1]; + if (Plan->SourceWidth < 32 || Plan->SourceWidth > 32) { + const std::uint64_t Mask = + Plan->SourceWidth == 64 + ? std::numeric_limits<std::uint64_t>::max() + : (std::uint64_t{1} << Plan->SourceWidth) - 1U; + auto WideType = LLT::makeAnyScalar(64); + if (!WideType) + return std::unexpected(WideType.error()); + MachineIRBuilder Builder(Function, Target); + auto MaskValue = + buildIntegerConstantBefore(Builder, Extension, 64, Mask); + if (!MaskValue) + return std::unexpected(MaskValue.error()); + auto MaskedAmount = buildBinaryBefore(Builder, Extension, + GenericOpcode::G_AND, *WideType, + Amount, *MaskValue); + if (!MaskedAmount) + return std::unexpected(MaskedAmount.error()); + Amount = *MaskedAmount; + } + auto SetOpcode = Extension.setOpcode( + Plan->SourceWidth == 32 ? targetOpcode(InstructionOpcode::G_SLLW) + : opcode(GenericOpcode::G_SHL)); + if (!SetOpcode) + return std::unexpected(SetOpcode.error()); + while (Extension.operandCount() > 1) { + auto Erased = Extension.eraseOperand(1); + if (!Erased) + return std::unexpected(Erased.error()); + } + for (Register Input : {Plan->WideInputs[0], Amount}) { + auto Operand = MachineOperand::registerOperand( + Input, RegisterOperandFlags{RegisterOperandFlag::Use}); + if (!Operand) + return std::unexpected(Operand.error()); + auto Added = Extension.addOperand(std::move(*Operand)); + if (!Added) + return std::unexpected(Added.error()); + } + MachineBasicBlock *BinaryBlock = Plan->Binary->parent(); + if (BinaryBlock == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V narrow shift is detached during legalization"); + return BinaryBlock->removeInstruction(*Plan->Binary); + } + + if (Plan->SourceWidth == 32 && + (Plan->Generic == GenericOpcode::G_ADD || Plan->Generic == GenericOpcode::G_SUB)) { + auto WideType = LLT::makeAnyScalar(64); + if (!WideType) + return std::unexpected(WideType.error()); + auto Temporary = Function.registerInfo().createVirtualRegister(*WideType); + if (!Temporary) + return std::unexpected(Temporary.error()); + auto Retargeted = Plan->Binary->operandAt(0)->setRegister(*Temporary); + if (!Retargeted) + return std::unexpected(Retargeted.error()); + auto SetOpcode = Extension.setOpcode(opcode(GenericOpcode::G_SEXT_INREG)); + if (!SetOpcode) + return std::unexpected(SetOpcode.error()); + auto ReplacedSource = Extension.operandAt(1)->setRegister(*Temporary); + if (!ReplacedSource) + return std::unexpected(ReplacedSource.error()); + auto Width = Extension.addOperand(MachineOperand::immediate(32)); + if (!Width) + return std::unexpected(Width.error()); + return {}; + } + + auto SetOpcode = Extension.setOpcode(opcode(Plan->Generic)); + if (!SetOpcode) + return std::unexpected(SetOpcode.error()); + while (Extension.operandCount() > 1) { + auto Erased = Extension.eraseOperand(1); + if (!Erased) + return std::unexpected(Erased.error()); + } + for (Register Input : Plan->WideInputs) { + auto Operand = MachineOperand::registerOperand( + Input, RegisterOperandFlags{RegisterOperandFlag::Use}); + if (!Operand) + return std::unexpected(Operand.error()); + auto Added = Extension.addOperand(std::move(*Operand)); + if (!Added) + return std::unexpected(Added.error()); + } + MachineBasicBlock *BinaryBlock = Plan->Binary->parent(); + if (BinaryBlock == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V narrow binary instruction is detached during legalization"); + auto Removed = BinaryBlock->removeInstruction(*Plan->Binary); + if (!Removed) + return std::unexpected(Removed.error()); + (void)Target; + return {}; +} + [[nodiscard]] bool fitsSigned(std::int64_t Value, std::uint32_t Bits) noexcept { const std::int64_t Minimum = -(std::int64_t{1} << (Bits - 1U)); const std::int64_t Maximum = (std::int64_t{1} << (Bits - 1U)) - 1; return Value >= Minimum && Value <= Maximum; } +[[nodiscard]] Expected<void> lowerConstantToPool(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) { + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + const MachineOperand *ImmediateOperand = Instruction.operandAt(1); + const auto *Immediate = ImmediateOperand == nullptr + ? nullptr + : ImmediateOperand->getIf<IntegerImmediateOperand>(); + if (Destination == nullptr || !Destination->isVirtual() || Immediate == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V constant-pool legalization has invalid operands"); + const auto DestinationInfo = Function.registerInfo().virtualRegisterInfo(*Destination); + if (!DestinationInfo || !DestinationInfo->get().Type || + DestinationInfo->get().Type->scalarBitWidth() != 64) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V constant-pool legalization requires a 64-bit destination"); + const LLT DestinationType = *DestinationInfo->get().Type; + auto Value = GenericConstantData::integer(Immediate->Value); + if (!Value) + return std::unexpected(Value.error()); + auto Align = Alignment::create(8); + if (!Align) + return std::unexpected(Align.error()); + MachineConstantPoolEntryDesc Description; + Description.Value = std::move(*Value); + Description.Size = 8; + Description.Align = *Align; + auto PoolIndex = Function.constantPool().addEntry(std::move(Description)); + if (!PoolIndex) + return std::unexpected(PoolIndex.error()); + + auto PointerType = LLT::makePointer(0, 64); + if (!PointerType) + return std::unexpected(PointerType.error()); + auto Address = Function.registerInfo().createVirtualRegister(*PointerType); + if (!Address) + return std::unexpected(Address.error()); + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto AddressInstruction = Builder.buildInstr(opcode(GenericOpcode::G_CONSTANT_POOL)); + if (!AddressInstruction) + return std::unexpected(AddressInstruction.error()); + auto AddressDef = AddressInstruction->addDef(*Address); + if (!AddressDef) + return std::unexpected(AddressDef.error()); + auto AddressPool = AddressInstruction->addConstantPool(*PoolIndex); + if (!AddressPool) + return std::unexpected(AddressPool.error()); + auto BuiltAddress = AddressInstruction->build(); + if (!BuiltAddress) + return std::unexpected(BuiltAddress.error()); + + MachineMemOperandDesc MemoryDescription; + MemoryDescription.Accesses = {MemoryAccessFlag::Load}; + MemoryDescription.Size = *MemorySize::fixed(8); + MemoryDescription.MemoryType = DestinationType; + MemoryDescription.Align = *Align; + MemoryDescription.Base = MemoryBase::constantPoolPseudo(); + auto Memory = Function.createMemOperand(std::move(MemoryDescription)); + if (!Memory) + return std::unexpected(Memory.error()); + auto SetOpcode = Instruction.setOpcode(opcode(GenericOpcode::G_LOAD)); + if (!SetOpcode) + return std::unexpected(SetOpcode.error()); + while (Instruction.operandCount() > 1) { + auto Erased = Instruction.eraseOperand(1); + if (!Erased) + return std::unexpected(Erased.error()); + } + auto AddressUse = MachineOperand::registerOperand( + *Address, RegisterOperandFlags{RegisterOperandFlag::Use}); + if (!AddressUse) + return std::unexpected(AddressUse.error()); + auto AddedAddress = Instruction.addOperand(std::move(*AddressUse)); + if (!AddedAddress) + return std::unexpected(AddedAddress.error()); + auto AddedMemory = Instruction.addMemOperand(**Memory); + if (!AddedMemory) + return std::unexpected(AddedMemory.error()); + return {}; +} + [[nodiscard]] bool isSupportedNonAtomicMemoryAccess(const MachineInstr &Instruction) noexcept { if (Instruction.memOperands().size() != 1) return false; @@ -236,6 +854,1611 @@ memoryAccessBytes(const MachineInstr &Instruction) noexcept { return std::bit_cast<std::int64_t>(Raw); } +[[nodiscard]] Expected<void> replaceRegisterUses(MachineFunction &Function, Register OldReg, + Register NewReg) { + std::vector<MachineOperand *> Uses; + for (MachineOperand &Operand : Function.registerInfo().useOperands(OldReg)) + Uses.push_back(&Operand); + for (MachineOperand *Operand : Uses) { + auto Replaced = Operand->setRegister(NewReg); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + return {}; +} + +[[nodiscard]] Expected<void> eraseInstruction(MachineInstr &Instruction) { + MachineBasicBlock *Block = Instruction.parent(); + if (Block == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V legalizer cannot erase a detached instruction"); + return Block->removeInstruction(Instruction); +} + +[[nodiscard]] bool allDefsUnused(const MachineInstr &Instruction, + const MachineFunction &Function) { + for (const MachineOperand &Operand : Instruction.operands()) { + const Register *Reg = Operand.getIf<Register>(); + if (Reg != nullptr && Operand.isDef() && Reg->isVirtual() && + !Function.registerInfo().useEmpty(*Reg)) + return false; + } + return true; +} + +void eraseDeadArtifacts(std::span<MachineInstr *const> Candidates, + const MachineFunction &Function) { + bool Changed = false; + do { + Changed = false; + for (auto It = Candidates.rbegin(); It != Candidates.rend(); ++It) { + MachineInstr *Instruction = *It; + if (Instruction == nullptr || Instruction->parent() == nullptr || + !allDefsUnused(*Instruction, Function)) + continue; + const Opcode Value = Instruction->opcode(); + if (Value != opcode(GenericOpcode::G_TRUNC) && + Value != opcode(GenericOpcode::G_ANYEXT) && + Value != opcode(GenericOpcode::G_SEXT) && Value != opcode(GenericOpcode::G_ZEXT) && + Value != opcode(GenericOpcode::G_MERGE_VALUES) && + Value != opcode(GenericOpcode::G_UNMERGE_VALUES) && + Value != opcode(GenericOpcode::COPY)) + continue; + if (eraseInstruction(*Instruction)) + Changed = true; + } + } while (Changed); +} + +[[nodiscard]] Expected<Register> buildIntegerConstantBefore(MachineIRBuilder &Builder, + MachineInstr &Anchor, + std::uint32_t Width, + std::uint64_t Value) { + auto Type = LLT::makeAnyScalar(Width); + if (!Type) + return std::unexpected(Type.error()); + auto Result = Builder.function().registerInfo().createVirtualRegister(*Type); + if (!Result) + return std::unexpected(Result.error()); + auto Immediate = IntegerImmediate::fromUnsigned(Width, Value); + if (!Immediate) + return std::unexpected(Immediate.error()); + auto Point = Builder.setInsertPointBefore(Anchor); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_CONSTANT)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(*Result); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addIntegerImmediate(std::move(*Immediate)); !Added) + return std::unexpected(Added.error()); + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + return *Result; +} + +[[nodiscard]] Expected<Register> buildUnaryBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, + GenericOpcode Generic, LLT Type, + Register Source) { + auto Result = Builder.function().registerInfo().createVirtualRegister(Type); + if (!Result) + return std::unexpected(Result.error()); + auto Point = Builder.setInsertPointBefore(Anchor); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(opcode(Generic)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(*Result); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Source); !Added) + return std::unexpected(Added.error()); + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + return *Result; +} + +[[nodiscard]] Expected<Register> buildBinaryBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, + GenericOpcode Generic, LLT Type, Register Left, + Register Right) { + auto Result = Builder.function().registerInfo().createVirtualRegister(Type); + if (!Result) + return std::unexpected(Result.error()); + auto Point = Builder.setInsertPointBefore(Anchor); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(opcode(Generic)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(*Result); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Left); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Right); !Added) + return std::unexpected(Added.error()); + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + return *Result; +} + +[[nodiscard]] Expected<void> rewriteUnary(MachineInstr &Instruction, GenericOpcode Generic, + Register Destination, Register Source) { + auto SetOpcode = Instruction.setOpcode(opcode(Generic)); + if (!SetOpcode) + return std::unexpected(SetOpcode.error()); + while (Instruction.operandCount() != 0) { + auto Erased = Instruction.eraseOperand(Instruction.operandCount() - 1U); + if (!Erased) + return std::unexpected(Erased.error()); + } + auto Def = MachineOperand::registerOperand( + Destination, RegisterOperandFlags{RegisterOperandFlag::Define}); + if (!Def) + return std::unexpected(Def.error()); + if (auto Added = Instruction.addOperand(std::move(*Def)); !Added) + return std::unexpected(Added.error()); + auto Use = MachineOperand::registerOperand(Source, RegisterOperandFlags{RegisterOperandFlag::Use}); + if (!Use) + return std::unexpected(Use.error()); + return Instruction.addOperand(std::move(*Use)); +} + +[[nodiscard]] Expected<void> rewriteBinary(MachineInstr &Instruction, GenericOpcode Generic, + Register Destination, Register Left, Register Right) { + auto Rewritten = rewriteUnary(Instruction, Generic, Destination, Left); + if (!Rewritten) + return std::unexpected(Rewritten.error()); + auto Use = MachineOperand::registerOperand(Right, RegisterOperandFlags{RegisterOperandFlag::Use}); + if (!Use) + return std::unexpected(Use.error()); + return Instruction.addOperand(std::move(*Use)); +} + +[[nodiscard]] bool isKnownZeroExtended(const MachineFunction &Function, Register Reg, + std::uint32_t ActiveBits, unsigned Depth = 0) { + if (Depth > 8 || !Reg.isVirtual()) + return false; + const auto Info = Function.registerInfo().virtualRegisterInfo(Reg); + if (!Info || !Info->get().Type) + return false; + const std::uint32_t Width = Info->get().Type->scalarBitWidth(); + if (Width <= ActiveBits) + return true; + const MachineOperand *Definition = Function.registerInfo().getUniqueVRegDef(Reg); + const MachineInstr *Def = Definition == nullptr ? nullptr : Definition->parent(); + if (Def == nullptr) + return false; + if (Def->opcode() == opcode(GenericOpcode::G_ASSERT_ZEXT)) { + const MachineOperand *BitsOperand = Def->operandAt(2); + auto Bits = BitsOperand == nullptr + ? Expected<std::int64_t>(std::unexpected( + Error(ErrorCode::InvalidArgument, "missing asserted extension width"))) + : signedImmediate(*BitsOperand); + return Bits && *Bits >= 0 && static_cast<std::uint64_t>(*Bits) <= ActiveBits; + } + if (Def->opcode() == opcode(GenericOpcode::G_ZEXT)) { + const MachineOperand *SourceOperand = Def->operandAt(1); + const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); + if (Source == nullptr) + return false; + const auto SourceInfo = Function.registerInfo().virtualRegisterInfo(*Source); + return SourceInfo && SourceInfo->get().Type && + SourceInfo->get().Type->scalarBitWidth() <= ActiveBits; + } + if (Def->opcode() == opcode(GenericOpcode::G_TRUNC)) { + const MachineOperand *SourceOperand = Def->operandAt(1); + const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); + return Source != nullptr && isKnownZeroExtended(Function, *Source, ActiveBits, Depth + 1U); + } + return false; +} + +[[nodiscard]] Expected<Register> +zeroExtendToRV64(MachineIRBuilder &Builder, MachineInstr &Anchor, MachineFunction &Function, + Register Source, std::vector<MachineInstr *> &DeadCandidates) { + const auto Info = Function.registerInfo().virtualRegisterInfo(Source); + if (!Info || !Info->get().Type) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V merge source has no scalar type"); + const LLT SourceType = *Info->get().Type; + if (SourceType.scalarBitWidth() == 64) + return Source; + const MachineOperand *Definition = Function.registerInfo().getUniqueVRegDef(Source); + MachineInstr *Def = Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); + if (Def != nullptr && Def->opcode() == opcode(GenericOpcode::G_TRUNC)) { + const MachineOperand *WideOperand = Def->operandAt(1); + const Register *Wide = WideOperand == nullptr ? nullptr : WideOperand->getIf<Register>(); + if (Wide != nullptr) { + const auto WideInfo = Function.registerInfo().virtualRegisterInfo(*Wide); + if (WideInfo && WideInfo->get().Type && WideInfo->get().Type->scalarBitWidth() == 64 && + isKnownZeroExtended(Function, *Wide, SourceType.scalarBitWidth())) { + DeadCandidates.push_back(Def); + return *Wide; + } + } + } + auto WideType = LLT::makeAnyScalar(64); + if (!WideType) + return std::unexpected(WideType.error()); + return buildUnaryBefore(Builder, Anchor, GenericOpcode::G_ZEXT, *WideType, Source); +} + +[[nodiscard]] MachineInstr *singleUseInstruction(const MachineFunction &Function, Register Reg) { + MachineInstr *Result = nullptr; + auto &RegisterInfo = const_cast<MachineRegisterInfo &>(Function.registerInfo()); + for (MachineOperand &Operand : RegisterInfo.useOperands(Reg)) { + MachineInstr *Instruction = Operand.parent(); + if (Instruction == nullptr || Instruction->parent() == nullptr) + continue; + if (Result != nullptr && Result != Instruction) + return nullptr; + Result = Instruction; + } + return Result; +} + +[[nodiscard]] Expected<void> lowerWideMergeValues(MachineInstr &Instruction, + MachineFunction &Function) { + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + const MachineOperand *LowOperand = Instruction.operandAt(1); + const Register *Low = LowOperand == nullptr ? nullptr : LowOperand->getIf<Register>(); + if (Destination == nullptr || Low == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide merge has invalid register operands"); + + std::vector<MachineInstr *> Truncations; + auto &RegisterInfo = Function.registerInfo(); + std::vector<MachineOperand *> Uses; + for (MachineOperand &Use : RegisterInfo.useOperands(*Destination)) + Uses.push_back(&Use); + for (MachineOperand *Use : Uses) { + MachineInstr *Trunc = Use->parent(); + if (Trunc == nullptr || Trunc->parent() == nullptr || + Trunc->opcode() != opcode(GenericOpcode::G_TRUNC) || Trunc->operandAt(1) != Use) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide merge is only representable through low-part truncation"); + const MachineOperand *TruncResultOperand = Trunc->operandAt(0); + const Register *TruncResult = + TruncResultOperand == nullptr ? nullptr : TruncResultOperand->getIf<Register>(); + const auto LowInfo = RegisterInfo.virtualRegisterInfo(*Low); + const auto ResultInfo = TruncResult == nullptr + ? Expected<std::reference_wrapper<const VirtualRegisterInfo>>( + std::unexpected(Error(ErrorCode::InvalidArgument, + "missing truncation result"))) + : RegisterInfo.virtualRegisterInfo(*TruncResult); + if (!LowInfo || !LowInfo->get().Type || !ResultInfo || !ResultInfo->get().Type || + ResultInfo->get().Type->scalarBitWidth() > LowInfo->get().Type->scalarBitWidth()) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide merge truncation spans more than its low source"); + if (*ResultInfo->get().Type == *LowInfo->get().Type) { + auto Replaced = replaceRegisterUses(Function, *TruncResult, *Low); + if (!Replaced) + return std::unexpected(Replaced.error()); + Truncations.push_back(Trunc); + } else { + auto Replaced = Use->setRegister(*Low); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + } + for (MachineInstr *Trunc : Truncations) { + auto Erased = eraseInstruction(*Trunc); + if (!Erased) + return std::unexpected(Erased.error()); + } + return eraseInstruction(Instruction); +} + +[[nodiscard]] Expected<void> lowerMergeValues(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) { + const std::optional<LLT> DestinationType = resultType(Function, Instruction); + if (!DestinationType || DestinationType->scalarBitWidth() == 0) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V merge destination has no scalar type"); + if (DestinationType->scalarBitWidth() > 64) + return lowerWideMergeValues(Instruction, Function); + + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + if (Destination == nullptr || Instruction.operandCount() < 3) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V merge has invalid register operands"); + + Register Result = *Destination; + MachineInstr *FoldedExtension = nullptr; + if (DestinationType->scalarBitWidth() < 64) { + MachineInstr *Use = singleUseInstruction(Function, *Destination); + if (Use != nullptr && Use->opcode() == opcode(GenericOpcode::G_ZEXT) && + operandType(Function, *Use, 0).transform([](LLT Type) { + return Type.scalarBitWidth() == 64; + }).value_or(false)) { + const MachineOperand *ExtendedOperand = Use->operandAt(0); + const Register *Extended = + ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); + if (Extended != nullptr) { + Result = *Extended; + FoldedExtension = Use; + } + } + } + + auto WideType = LLT::makeAnyScalar(64); + if (!WideType) + return std::unexpected(WideType.error()); + MachineIRBuilder Builder(Function, Target); + std::vector<MachineInstr *> DeadCandidates; + std::vector<Register> Terms; + Terms.reserve(Instruction.operandCount() - 1U); + for (std::size_t Index = 1; Index < Instruction.operandCount(); ++Index) { + const MachineOperand *Operand = Instruction.operandAt(Index); + const Register *Source = Operand == nullptr ? nullptr : Operand->getIf<Register>(); + if (Source == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V merge source is not a register"); + auto WideSource = zeroExtendToRV64(Builder, Instruction, Function, *Source, DeadCandidates); + if (!WideSource) + return std::unexpected(WideSource.error()); + if (Index == 1) { + Terms.push_back(*WideSource); + continue; + } + const std::optional<LLT> SourceType = registerType(Function, *Operand); + if (!SourceType) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V merge source has no scalar type"); + const std::uint64_t Shift = + static_cast<std::uint64_t>(SourceType->scalarBitWidth()) * (Index - 1U); + auto Amount = buildIntegerConstantBefore(Builder, Instruction, 64, Shift); + if (!Amount) + return std::unexpected(Amount.error()); + auto Shifted = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_SHL, *WideType, + *WideSource, *Amount); + if (!Shifted) + return std::unexpected(Shifted.error()); + Terms.push_back(*Shifted); + } + + Register Accumulator = Terms.front(); + for (std::size_t Index = 1; Index + 1U < Terms.size(); ++Index) { + auto Combined = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_OR, *WideType, + Accumulator, Terms[Index]); + if (!Combined) + return std::unexpected(Combined.error()); + Accumulator = *Combined; + } + if (DestinationType->scalarBitWidth() == 64 || FoldedExtension != nullptr) { + auto Rewritten = rewriteBinary(Instruction, GenericOpcode::G_OR, Result, Accumulator, + Terms.back()); + if (!Rewritten) + return std::unexpected(Rewritten.error()); + } else { + auto Combined = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_OR, *WideType, + Accumulator, Terms.back()); + if (!Combined) + return std::unexpected(Combined.error()); + auto Rewritten = rewriteUnary(Instruction, GenericOpcode::G_TRUNC, *Destination, *Combined); + if (!Rewritten) + return std::unexpected(Rewritten.error()); + } + if (FoldedExtension != nullptr) { + auto Erased = eraseInstruction(*FoldedExtension); + if (!Erased) + return std::unexpected(Erased.error()); + } + eraseDeadArtifacts(DeadCandidates, Function); + return {}; +} + +[[nodiscard]] Expected<void> +foldWideExtendedUnmerge(MachineInstr &Instruction, MachineFunction &Function) { + const std::size_t OutputCount = Instruction.operandCount() - 1U; + const MachineOperand *SourceOperand = Instruction.operandAt(OutputCount); + const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); + const MachineOperand *Definition = + Source == nullptr ? nullptr : Function.registerInfo().getUniqueVRegDef(*Source); + MachineInstr *Extension = + Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); + if (Extension == nullptr || + (Extension->opcode() != opcode(GenericOpcode::G_ZEXT) && + Extension->opcode() != opcode(GenericOpcode::G_ANYEXT) && + Extension->opcode() != opcode(GenericOpcode::G_SEXT))) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide unmerge requires an extension-defined source"); + const MachineOperand *NarrowOperand = Extension->operandAt(1); + const Register *Narrow = NarrowOperand == nullptr ? nullptr : NarrowOperand->getIf<Register>(); + if (Narrow == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide unmerge extension has no register source"); + const auto NarrowInfo = Function.registerInfo().virtualRegisterInfo(*Narrow); + if (!NarrowInfo || !NarrowInfo->get().Type) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide unmerge extension source has no type"); + const std::uint32_t NarrowWidth = NarrowInfo->get().Type->scalarBitWidth(); + + std::vector<MachineInstr *> DeadUsers; + for (std::size_t Index = 0; Index < OutputCount; ++Index) { + const MachineOperand *OutputOperand = Instruction.operandAt(Index); + const Register *Output = OutputOperand == nullptr ? nullptr : OutputOperand->getIf<Register>(); + if (Output == nullptr || Function.registerInfo().useEmpty(*Output)) + continue; + const auto OutputInfo = Function.registerInfo().virtualRegisterInfo(*Output); + if (!OutputInfo || !OutputInfo->get().Type) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide unmerge output has no type"); + const std::uint64_t Offset = + static_cast<std::uint64_t>(Index) * OutputInfo->get().Type->scalarBitWidth(); + if (Offset != 0) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide unmerge cannot materialize a used high wide part"); + if (OutputInfo->get().Type->scalarBitWidth() == NarrowWidth) { + auto Replaced = replaceRegisterUses(Function, *Output, *Narrow); + if (!Replaced) + return std::unexpected(Replaced.error()); + continue; + } + if (OutputInfo->get().Type->scalarBitWidth() < NarrowWidth) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide unmerge output unexpectedly narrows its source"); + std::vector<MachineOperand *> Uses; + for (MachineOperand &Use : Function.registerInfo().useOperands(*Output)) + Uses.push_back(&Use); + for (MachineOperand *Use : Uses) { + MachineInstr *Trunc = Use->parent(); + if (Trunc == nullptr || Trunc->parent() == nullptr || + Trunc->opcode() != opcode(GenericOpcode::G_TRUNC) || Trunc->operandAt(1) != Use) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide unmerge result is only representable through truncation"); + const MachineOperand *ResultOperand = Trunc->operandAt(0); + const Register *Result = ResultOperand == nullptr ? nullptr : ResultOperand->getIf<Register>(); + const auto ResultInfo = Result == nullptr + ? Expected<std::reference_wrapper<const VirtualRegisterInfo>>( + std::unexpected(Error(ErrorCode::InvalidArgument, + "missing truncation result"))) + : Function.registerInfo().virtualRegisterInfo(*Result); + if (!ResultInfo || !ResultInfo->get().Type || + ResultInfo->get().Type->scalarBitWidth() > NarrowWidth) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide unmerge truncation exceeds its source width"); + if (ResultInfo->get().Type->scalarBitWidth() == NarrowWidth) { + auto Replaced = replaceRegisterUses(Function, *Result, *Narrow); + if (!Replaced) + return std::unexpected(Replaced.error()); + DeadUsers.push_back(Trunc); + } else { + auto Replaced = Use->setRegister(*Narrow); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + } + } + for (MachineInstr *User : DeadUsers) { + auto Erased = eraseInstruction(*User); + if (!Erased) + return std::unexpected(Erased.error()); + } + auto Erased = eraseInstruction(Instruction); + if (!Erased) + return std::unexpected(Erased.error()); + if (Function.registerInfo().useEmpty(*Source)) + return eraseInstruction(*Extension); + return {}; +} + +[[nodiscard]] Expected<void> lowerUnmergeValues(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) { + if (Instruction.operandCount() < 3) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V unmerge has invalid register operands"); + const std::size_t OutputCount = Instruction.operandCount() - 1U; + const std::optional<LLT> OutputType = operandType(Function, Instruction, 0); + const std::optional<LLT> SourceType = operandType(Function, Instruction, OutputCount); + if (!OutputType || !SourceType) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V unmerge operands have no scalar types"); + if (SourceType->scalarBitWidth() > 64 || OutputType->scalarBitWidth() > 64) + return foldWideExtendedUnmerge(Instruction, Function); + + const MachineOperand *SourceOperand = Instruction.operandAt(OutputCount); + const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); + if (Source == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V unmerge source is not a register"); + auto WideType = LLT::makeAnyScalar(64); + if (!WideType) + return std::unexpected(WideType.error()); + MachineIRBuilder Builder(Function, Target); + std::vector<MachineInstr *> FoldedExtensions; + for (std::size_t Index = 0; Index < OutputCount; ++Index) { + const MachineOperand *OutputOperand = Instruction.operandAt(Index); + const Register *Output = OutputOperand == nullptr ? nullptr : OutputOperand->getIf<Register>(); + if (Output == nullptr || Function.registerInfo().useEmpty(*Output)) + continue; + Register Extracted = *Source; + const std::uint64_t Offset = + static_cast<std::uint64_t>(Index) * OutputType->scalarBitWidth(); + if (Offset != 0) { + auto Amount = buildIntegerConstantBefore(Builder, Instruction, 64, Offset); + if (!Amount) + return std::unexpected(Amount.error()); + auto Shifted = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_LSHR, *WideType, + *Source, *Amount); + if (!Shifted) + return std::unexpected(Shifted.error()); + Extracted = *Shifted; + } + + MachineInstr *Extension = singleUseInstruction(Function, *Output); + const bool FoldZExt = Extension != nullptr && + Extension->opcode() == opcode(GenericOpcode::G_ZEXT) && + operandType(Function, *Extension, 0) + .transform([](LLT Type) { return Type.scalarBitWidth() == 64; }) + .value_or(false); + if (FoldZExt) { + const MachineOperand *ExtendedOperand = Extension->operandAt(0); + const Register *Extended = + ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); + if (Extended == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V unmerge extension has no destination"); + const std::uint64_t Mask = OutputType->scalarBitWidth() == 64 + ? std::numeric_limits<std::uint64_t>::max() + : (std::uint64_t{1} << OutputType->scalarBitWidth()) - 1U; + auto MaskReg = buildIntegerConstantBefore(Builder, Instruction, 64, Mask); + if (!MaskReg) + return std::unexpected(MaskReg.error()); + auto Masked = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_AND, *WideType, + Extracted, *MaskReg); + if (!Masked) + return std::unexpected(Masked.error()); + auto Replaced = replaceRegisterUses(Function, *Extended, *Masked); + if (!Replaced) + return std::unexpected(Replaced.error()); + FoldedExtensions.push_back(Extension); + continue; + } + auto Truncated = buildUnaryBefore(Builder, Instruction, GenericOpcode::G_TRUNC, *OutputType, + Extracted); + if (!Truncated) + return std::unexpected(Truncated.error()); + auto Replaced = replaceRegisterUses(Function, *Output, *Truncated); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + for (MachineInstr *Extension : FoldedExtensions) { + auto Erased = eraseInstruction(*Extension); + if (!Erased) + return std::unexpected(Erased.error()); + } + return eraseInstruction(Instruction); +} + +struct WideValueParts final { + std::uint32_t BitWidth = 0; + std::vector<Register> Parts; + std::vector<MachineInstr *> Artifacts; +}; + +[[nodiscard]] Expected<WideValueParts> collectWideValueParts(Register Value, + MachineFunction &Function, + unsigned Depth = 0) { + if (Depth > 16) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide-value artifact chain is too deep"); + const auto Info = Function.registerInfo().virtualRegisterInfo(Value); + if (!Info || !Info->get().Type) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide value has no scalar type"); + const LLT Type = *Info->get().Type; + if ((!Type.isInteger() && !Type.isAnyScalar()) || Type.scalarBitWidth() == 0) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide-value decomposition requires an integer scalar"); + if (Type.scalarBitWidth() <= 64) + return WideValueParts{ + .BitWidth = Type.scalarBitWidth(), .Parts = {Value}, .Artifacts = {}}; + + const MachineOperand *Definition = Function.registerInfo().getUniqueVRegDef(Value); + MachineInstr *Def = + Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); + if (Def == nullptr) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide value has no decomposable definition"); + if (Def->opcode() == opcode(GenericOpcode::G_MERGE_VALUES)) { + WideValueParts Result; + Result.BitWidth = Type.scalarBitWidth(); + for (std::size_t Index = 1; Index < Def->operandCount(); ++Index) { + const MachineOperand *Operand = Def->operandAt(Index); + const Register *Part = Operand == nullptr ? nullptr : Operand->getIf<Register>(); + const std::optional<LLT> PartType = + Operand == nullptr ? std::nullopt : registerType(Function, *Operand); + if (Part == nullptr || !PartType || PartType->scalarBitWidth() != 64) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide merge must already use 64-bit limbs"); + Result.Parts.push_back(*Part); + } + Result.Artifacts.push_back(Def); + return Result; + } + if (Def->opcode() == opcode(GenericOpcode::G_TRUNC)) { + const MachineOperand *SourceOperand = Def->operandAt(1); + const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); + if (Source == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide truncation has no source register"); + auto Result = collectWideValueParts(*Source, Function, Depth + 1U); + if (!Result) + return std::unexpected(Result.error()); + Result->BitWidth = Type.scalarBitWidth(); + const std::size_t RequiredParts = (Type.scalarBitWidth() + 63U) / 64U; + if (Result->Parts.size() < RequiredParts) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide truncation source has too few limbs"); + Result->Parts.resize(RequiredParts); + Result->Artifacts.push_back(Def); + return Result; + } + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide value is not defined by merge/trunc artifacts"); +} + +struct WideResultPlan final { + std::vector<Register> Outputs; + MachineInstr *Extension = nullptr; + MachineInstr *Unmerge = nullptr; +}; + +[[nodiscard]] Expected<WideResultPlan> wideResultPlan(Register Result, + MachineFunction &Function, + std::size_t RequiredParts) { + MachineInstr *Use = singleUseInstruction(Function, Result); + WideResultPlan Plan; + if (Use != nullptr && + (Use->opcode() == opcode(GenericOpcode::G_ANYEXT) || + Use->opcode() == opcode(GenericOpcode::G_SEXT) || + Use->opcode() == opcode(GenericOpcode::G_ZEXT))) { + Plan.Extension = Use; + const MachineOperand *ExtendedOperand = Use->operandAt(0); + const Register *Extended = + ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); + Use = Extended == nullptr ? nullptr : singleUseInstruction(Function, *Extended); + } + if (Use == nullptr || Use->opcode() != opcode(GenericOpcode::G_UNMERGE_VALUES)) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide result must feed an unmerge artifact"); + Plan.Unmerge = Use; + const std::size_t OutputCount = Use->operandCount() - 1U; + if (OutputCount < RequiredParts) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide result unmerge has too few outputs"); + Plan.Outputs.reserve(RequiredParts); + for (std::size_t Index = 0; Index < RequiredParts; ++Index) { + const MachineOperand *Operand = Use->operandAt(Index); + const Register *Output = Operand == nullptr ? nullptr : Operand->getIf<Register>(); + const std::optional<LLT> Type = + Operand == nullptr ? std::nullopt : registerType(Function, *Operand); + if (Output == nullptr || !Type || Type->scalarBitWidth() != 64) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V wide result must unmerge to 64-bit limbs"); + Plan.Outputs.push_back(*Output); + } + return Plan; +} + +[[nodiscard]] Expected<Register> +buildIntegerCompareBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, + IntegerPredicate Predicate, Register Left, Register Right) { + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + auto Result = Builder.function().registerInfo().createVirtualRegister(*Type); + if (!Result) + return std::unexpected(Result.error()); + auto Point = Builder.setInsertPointBefore(Anchor); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_ICMP)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(*Result); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addIntegerPredicate(Predicate); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Left); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Right); !Added) + return std::unexpected(Added.error()); + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + return *Result; +} + +[[nodiscard]] Expected<Register> +buildSelectBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, LLT Type, + Register Condition, Register TrueValue, Register FalseValue) { + auto Result = Builder.function().registerInfo().createVirtualRegister(Type); + if (!Result) + return std::unexpected(Result.error()); + auto Point = Builder.setInsertPointBefore(Anchor); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_SELECT)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(*Result); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Condition); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(TrueValue); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(FalseValue); !Added) + return std::unexpected(Added.error()); + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + return *Result; +} + +struct TwoLimbShiftResult final { + Register Low; + Register High; +}; + +[[nodiscard]] Expected<TwoLimbShiftResult> +emitTwoLimbLeftShiftBody(MachineIRBuilder &Builder, MachineInstr &Anchor, + Register Low, Register High, Register Amount, + Register AmountExcess, Register AmountLack, + Register Zero, Register IsShort, Register IsZero) { + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + auto LowShort = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, Low, Amount); + if (!LowShort) + return std::unexpected(LowShort.error()); + auto CrossLow = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_LSHR, *Type, Low, AmountLack); + if (!CrossLow) + return std::unexpected(CrossLow.error()); + auto HighShort = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, High, Amount); + if (!HighShort) + return std::unexpected(HighShort.error()); + auto ShortHigh = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_OR, *Type, + *CrossLow, *HighShort); + if (!ShortHigh) + return std::unexpected(ShortHigh.error()); + auto LongHigh = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, Low, AmountExcess); + if (!LongHigh) + return std::unexpected(LongHigh.error()); + auto ResultLow = + buildSelectBefore(Builder, Anchor, *Type, IsShort, *LowShort, Zero); + if (!ResultLow) + return std::unexpected(ResultLow.error()); + auto ShiftedHigh = + buildSelectBefore(Builder, Anchor, *Type, IsShort, *ShortHigh, *LongHigh); + if (!ShiftedHigh) + return std::unexpected(ShiftedHigh.error()); + auto ResultHigh = + buildSelectBefore(Builder, Anchor, *Type, IsZero, High, *ShiftedHigh); + if (!ResultHigh) + return std::unexpected(ResultHigh.error()); + return TwoLimbShiftResult{.Low = *ResultLow, .High = *ResultHigh}; +} + +[[nodiscard]] Expected<TwoLimbShiftResult> +emitTwoLimbLeftShift(MachineIRBuilder &Builder, MachineInstr &Anchor, + Register Low, Register High, Register Amount) { + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + auto Boundary = buildIntegerConstantBefore(Builder, Anchor, 64, 64); + if (!Boundary) + return std::unexpected(Boundary.error()); + auto AmountExcess = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, + Amount, *Boundary); + if (!AmountExcess) + return std::unexpected(AmountExcess.error()); + auto AmountLack = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, + *Boundary, Amount); + if (!AmountLack) + return std::unexpected(AmountLack.error()); + auto Zero = buildIntegerConstantBefore(Builder, Anchor, 64, 0); + if (!Zero) + return std::unexpected(Zero.error()); + auto IsShort = buildIntegerCompareBefore(Builder, Anchor, + IntegerPredicate::UnsignedLessThan, + Amount, *Boundary); + if (!IsShort) + return std::unexpected(IsShort.error()); + auto IsZero = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, + Amount, *Zero); + if (!IsZero) + return std::unexpected(IsZero.error()); + return emitTwoLimbLeftShiftBody(Builder, Anchor, Low, High, Amount, + *AmountExcess, *AmountLack, *Zero, *IsShort, + *IsZero); +} + +[[nodiscard]] Expected<Register> +emitLowLimbOfTwoLimbLogicalRightShift( + MachineIRBuilder &Builder, MachineInstr &Anchor, Register Low, Register High, + Register Amount, Register Boundary, Register Zero) { + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + auto AmountExcess = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, + Amount, Boundary); + if (!AmountExcess) + return std::unexpected(AmountExcess.error()); + auto AmountLack = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, + Boundary, Amount); + if (!AmountLack) + return std::unexpected(AmountLack.error()); + auto IsShort = buildIntegerCompareBefore(Builder, Anchor, + IntegerPredicate::UnsignedLessThan, + Amount, Boundary); + if (!IsShort) + return std::unexpected(IsShort.error()); + auto IsZero = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, + Amount, Zero); + if (!IsZero) + return std::unexpected(IsZero.error()); + auto LowShort = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_LSHR, *Type, Low, Amount); + if (!LowShort) + return std::unexpected(LowShort.error()); + auto HighShort = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, High, *AmountLack); + if (!HighShort) + return std::unexpected(HighShort.error()); + auto ShortValue = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_OR, *Type, + *LowShort, *HighShort); + if (!ShortValue) + return std::unexpected(ShortValue.error()); + auto LongValue = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_LSHR, *Type, + High, *AmountExcess); + if (!LongValue) + return std::unexpected(LongValue.error()); + auto Shifted = buildSelectBefore(Builder, Anchor, *Type, *IsShort, *ShortValue, + *LongValue); + if (!Shifted) + return std::unexpected(Shifted.error()); + return buildSelectBefore(Builder, Anchor, *Type, *IsZero, Low, *Shifted); +} + +[[nodiscard]] Expected<std::vector<Register>> +emitThreeLimbLeftShift(MachineIRBuilder &Builder, MachineInstr &Anchor, + std::span<const Register> Parts, Register Amount) { + if (Parts.size() != 3) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V three-limb shift requires exactly three limbs"); + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + + auto OuterBoundary = buildIntegerConstantBefore(Builder, Anchor, 64, 128); + if (!OuterBoundary) + return std::unexpected(OuterBoundary.error()); + auto OuterExcess = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, + Amount, *OuterBoundary); + if (!OuterExcess) + return std::unexpected(OuterExcess.error()); + auto OuterLack = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, + *OuterBoundary, Amount); + if (!OuterLack) + return std::unexpected(OuterLack.error()); + auto Zero = buildIntegerConstantBefore(Builder, Anchor, 64, 0); + if (!Zero) + return std::unexpected(Zero.error()); + auto OuterIsShort = buildIntegerCompareBefore( + Builder, Anchor, IntegerPredicate::UnsignedLessThan, Amount, *OuterBoundary); + if (!OuterIsShort) + return std::unexpected(OuterIsShort.error()); + auto OuterIsZero = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, + Amount, *Zero); + if (!OuterIsZero) + return std::unexpected(OuterIsZero.error()); + + auto InnerBoundary = buildIntegerConstantBefore(Builder, Anchor, 64, 64); + if (!InnerBoundary) + return std::unexpected(InnerBoundary.error()); + auto InnerExcess = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, + Amount, *InnerBoundary); + if (!InnerExcess) + return std::unexpected(InnerExcess.error()); + auto InnerLack = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, + *InnerBoundary, Amount); + if (!InnerLack) + return std::unexpected(InnerLack.error()); + auto InnerIsShort = buildIntegerCompareBefore( + Builder, Anchor, IntegerPredicate::UnsignedLessThan, Amount, *InnerBoundary); + if (!InnerIsShort) + return std::unexpected(InnerIsShort.error()); + auto InnerIsZero = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, + Amount, *Zero); + if (!InnerIsZero) + return std::unexpected(InnerIsZero.error()); + auto Lower = emitTwoLimbLeftShiftBody( + Builder, Anchor, Parts[0], Parts[1], Amount, *InnerExcess, *InnerLack, + *Zero, *InnerIsShort, *InnerIsZero); + if (!Lower) + return std::unexpected(Lower.error()); + + auto Cross = emitLowLimbOfTwoLimbLogicalRightShift( + Builder, Anchor, Parts[0], Parts[1], *OuterLack, *InnerBoundary, *Zero); + if (!Cross) + return std::unexpected(Cross.error()); + auto HighIsShort = buildIntegerCompareBefore( + Builder, Anchor, IntegerPredicate::UnsignedLessThan, Amount, *InnerBoundary); + if (!HighIsShort) + return std::unexpected(HighIsShort.error()); + auto HighShort = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, + Parts[2], Amount); + if (!HighShort) + return std::unexpected(HighShort.error()); + auto BoundedHigh = buildSelectBefore(Builder, Anchor, *Type, *HighIsShort, + *HighShort, *Zero); + if (!BoundedHigh) + return std::unexpected(BoundedHigh.error()); + auto ShortHigh = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_OR, *Type, + *Cross, *BoundedHigh); + if (!ShortHigh) + return std::unexpected(ShortHigh.error()); + + auto LongIsShort = buildIntegerCompareBefore( + Builder, Anchor, IntegerPredicate::UnsignedLessThan, *OuterExcess, + *InnerBoundary); + if (!LongIsShort) + return std::unexpected(LongIsShort.error()); + auto LongShift = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, + Parts[0], *OuterExcess); + if (!LongShift) + return std::unexpected(LongShift.error()); + auto LongHigh = buildSelectBefore(Builder, Anchor, *Type, *LongIsShort, + *LongShift, *Zero); + if (!LongHigh) + return std::unexpected(LongHigh.error()); + + auto ResultLow = buildSelectBefore(Builder, Anchor, *Type, *OuterIsShort, + Lower->Low, *Zero); + if (!ResultLow) + return std::unexpected(ResultLow.error()); + auto ResultMiddle = buildSelectBefore(Builder, Anchor, *Type, *OuterIsShort, + Lower->High, *Zero); + if (!ResultMiddle) + return std::unexpected(ResultMiddle.error()); + auto ShiftedHigh = buildSelectBefore(Builder, Anchor, *Type, *OuterIsShort, + *ShortHigh, *LongHigh); + if (!ShiftedHigh) + return std::unexpected(ShiftedHigh.error()); + auto ResultHigh = buildSelectBefore(Builder, Anchor, *Type, *OuterIsZero, + Parts[2], *ShiftedHigh); + if (!ResultHigh) + return std::unexpected(ResultHigh.error()); + return std::vector<Register>{*ResultLow, *ResultMiddle, *ResultHigh}; +} + +[[nodiscard]] Expected<void> lowerWideShiftLeft(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) { + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const MachineOperand *ValueOperand = Instruction.operandAt(1); + const MachineOperand *AmountOperand = Instruction.operandAt(2); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + const Register *Value = ValueOperand == nullptr ? nullptr : ValueOperand->getIf<Register>(); + const Register *Amount = AmountOperand == nullptr ? nullptr : AmountOperand->getIf<Register>(); + if (Destination == nullptr || Value == nullptr || Amount == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide shift has invalid register operands"); + auto ValueParts = collectWideValueParts(*Value, Function); + if (!ValueParts) + return std::unexpected(ValueParts.error()); + auto AmountParts = collectWideValueParts(*Amount, Function); + if (!AmountParts) + return std::unexpected(AmountParts.error()); + if (ValueParts->Parts.size() < 2 || AmountParts->Parts.empty()) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide shift has an invalid limb decomposition"); + auto ResultPlan = + wideResultPlan(*Destination, Function, ValueParts->Parts.size()); + if (!ResultPlan) + return std::unexpected(ResultPlan.error()); + + MachineIRBuilder Builder(Function, Target); + Expected<std::vector<Register>> Results = + ValueParts->Parts.size() == 2 + ? emitTwoLimbLeftShift(Builder, Instruction, ValueParts->Parts[0], + ValueParts->Parts[1], AmountParts->Parts[0]) + .transform([](TwoLimbShiftResult Result) { + return std::vector<Register>{Result.Low, Result.High}; + }) + : ValueParts->Parts.size() == 3 + ? emitThreeLimbLeftShift(Builder, Instruction, ValueParts->Parts, + AmountParts->Parts[0]) + : Expected<std::vector<Register>>(std::unexpected( + Error(ErrorCode::Unsupported, + "RISC-V wide shift currently requires two or three 64-bit limbs"))); + if (!Results) + return std::unexpected(Results.error()); + for (std::size_t Index = 0; Index < Results->size(); ++Index) { + auto Replaced = replaceRegisterUses(Function, ResultPlan->Outputs[Index], + (*Results)[Index]); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + if (auto Erased = eraseInstruction(*ResultPlan->Unmerge); !Erased) + return std::unexpected(Erased.error()); + if (ResultPlan->Extension != nullptr) { + if (auto Erased = eraseInstruction(*ResultPlan->Extension); !Erased) + return std::unexpected(Erased.error()); + } + if (auto Erased = eraseInstruction(Instruction); !Erased) + return std::unexpected(Erased.error()); + for (std::size_t Index = 1; Index < AmountParts->Parts.size(); ++Index) { + const MachineOperand *Definition = + Function.registerInfo().getUniqueVRegDef(AmountParts->Parts[Index]); + if (Definition != nullptr && Definition->parent() != nullptr) + AmountParts->Artifacts.push_back( + const_cast<MachineInstr *>(Definition->parent())); + } + ValueParts->Artifacts.insert(ValueParts->Artifacts.end(), + AmountParts->Artifacts.begin(), + AmountParts->Artifacts.end()); + eraseDeadArtifacts(ValueParts->Artifacts, Function); + return {}; +} + +[[nodiscard]] Expected<std::vector<Register>> +emitWideAdd(MachineIRBuilder &Builder, MachineInstr &Anchor, std::span<const Register> Left, + std::span<const Register> Right) { + if (Left.size() != Right.size() || Left.size() < 2) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide add requires matching multi-limb operands"); + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + std::vector<Register> Results; + Results.reserve(Left.size()); + auto Low = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_ADD, *Type, Left[0], Right[0]); + if (!Low) + return std::unexpected(Low.error()); + auto Carry = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::UnsignedLessThan, + *Low, Right[0]); + if (!Carry) + return std::unexpected(Carry.error()); + auto LowCopy = buildUnaryBefore(Builder, Anchor, GenericOpcode::COPY, *Type, *Low); + if (!LowCopy) + return std::unexpected(LowCopy.error()); + Results.push_back(*LowCopy); + for (std::size_t Index = 1; Index < Left.size(); ++Index) { + auto Base = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_ADD, *Type, Left[Index], Right[Index]); + if (!Base) + return std::unexpected(Base.error()); + std::optional<Register> BaseCarry; + if (Index + 1U < Left.size()) { + auto Compared = buildIntegerCompareBefore(Builder, Anchor, + IntegerPredicate::UnsignedLessThan, *Base, + Left[Index]); + if (!Compared) + return std::unexpected(Compared.error()); + BaseCarry = *Compared; + } + auto Sum = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_ADD, *Type, *Base, *Carry); + if (!Sum) + return std::unexpected(Sum.error()); + if (Index + 1U < Left.size()) { + auto Zero = buildIntegerConstantBefore(Builder, Anchor, 64, 0); + if (!Zero) + return std::unexpected(Zero.error()); + auto IsZero = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, *Sum, *Zero); + if (!IsZero) + return std::unexpected(IsZero.error()); + auto CarryFromIncoming = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_AND, *Type, *IsZero, *Carry); + if (!CarryFromIncoming) + return std::unexpected(CarryFromIncoming.error()); + auto NextCarry = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_OR, *Type, *BaseCarry, + *CarryFromIncoming); + if (!NextCarry) + return std::unexpected(NextCarry.error()); + Carry = *NextCarry; + } + auto Copy = buildUnaryBefore(Builder, Anchor, GenericOpcode::COPY, *Type, *Sum); + if (!Copy) + return std::unexpected(Copy.error()); + Results.push_back(*Copy); + } + return Results; +} + +[[nodiscard]] Expected<std::vector<Register>> +emitWideSub(MachineIRBuilder &Builder, MachineInstr &Anchor, std::span<const Register> Left, + std::span<const Register> Right) { + if (Left.size() != Right.size() || Left.size() < 2) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide sub requires matching multi-limb operands"); + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + std::vector<Register> Results; + Results.reserve(Left.size()); + auto Low = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, Left[0], Right[0]); + if (!Low) + return std::unexpected(Low.error()); + Results.push_back(*Low); + auto Borrow = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::UnsignedLessThan, + Left[0], Right[0]); + if (!Borrow) + return std::unexpected(Borrow.error()); + for (std::size_t Index = 1; Index < Left.size(); ++Index) { + auto Base = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, Left[Index], Right[Index]); + if (!Base) + return std::unexpected(Base.error()); + std::optional<Register> BaseBorrow; + if (Index + 1U < Left.size()) { + auto Compared = buildIntegerCompareBefore(Builder, Anchor, + IntegerPredicate::UnsignedGreaterThan, *Base, + Left[Index]); + if (!Compared) + return std::unexpected(Compared.error()); + BaseBorrow = *Compared; + } + auto Difference = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, *Base, *Borrow); + if (!Difference) + return std::unexpected(Difference.error()); + Results.push_back(*Difference); + if (Index + 1U < Left.size()) { + auto Zero = buildIntegerConstantBefore(Builder, Anchor, 64, 0); + if (!Zero) + return std::unexpected(Zero.error()); + auto IsZero = + buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, *Base, *Zero); + if (!IsZero) + return std::unexpected(IsZero.error()); + auto BorrowFromIncoming = + buildBinaryBefore(Builder, Anchor, GenericOpcode::G_AND, *Type, *IsZero, *Borrow); + if (!BorrowFromIncoming) + return std::unexpected(BorrowFromIncoming.error()); + auto NextBorrow = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_OR, *Type, *BaseBorrow, + *BorrowFromIncoming); + if (!NextBorrow) + return std::unexpected(NextBorrow.error()); + Borrow = *NextBorrow; + } + } + return Results; +} + +[[nodiscard]] Expected<std::vector<Register>> +emitWideBitwise(MachineIRBuilder &Builder, MachineInstr &Anchor, GenericOpcode Generic, + std::span<const Register> Left, std::span<const Register> Right) { + if (Left.size() != Right.size() || Left.size() < 2) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide bitwise operation requires matching multi-limb operands"); + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + std::vector<Register> Results; + Results.reserve(Left.size()); + for (std::size_t Index = 0; Index < Left.size(); ++Index) { + auto Part = buildBinaryBefore(Builder, Anchor, Generic, *Type, Left[Index], Right[Index]); + if (!Part) + return std::unexpected(Part.error()); + Results.push_back(*Part); + } + return Results; +} + +[[nodiscard]] Expected<void> lowerWideBinary(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target, + GenericOpcode Generic) { + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const MachineOperand *LeftOperand = Instruction.operandAt(1); + const MachineOperand *RightOperand = Instruction.operandAt(2); + 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>(); + if (Destination == nullptr || Left == nullptr || Right == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide binary instruction has invalid operands"); + auto LeftParts = collectWideValueParts(*Left, Function); + if (!LeftParts) + return std::unexpected(LeftParts.error()); + auto RightParts = collectWideValueParts(*Right, Function); + if (!RightParts) + return std::unexpected(RightParts.error()); + if (LeftParts->Parts.size() != RightParts->Parts.size()) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide binary operands have different limb counts"); + auto ResultPlan = wideResultPlan(*Destination, Function, LeftParts->Parts.size()); + if (!ResultPlan) + return std::unexpected(ResultPlan.error()); + + MachineIRBuilder Builder(Function, Target); + Expected<std::vector<Register>> Results = + Generic == GenericOpcode::G_ADD + ? emitWideAdd(Builder, Instruction, LeftParts->Parts, RightParts->Parts) + : Generic == GenericOpcode::G_SUB + ? emitWideSub(Builder, Instruction, LeftParts->Parts, RightParts->Parts) + : emitWideBitwise(Builder, Instruction, Generic, LeftParts->Parts, RightParts->Parts); + if (!Results) + return std::unexpected(Results.error()); + for (std::size_t Index = 0; Index < Results->size(); ++Index) { + auto Replaced = replaceRegisterUses(Function, ResultPlan->Outputs[Index], (*Results)[Index]); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + if (auto Erased = eraseInstruction(*ResultPlan->Unmerge); !Erased) + return std::unexpected(Erased.error()); + if (ResultPlan->Extension != nullptr) { + if (auto Erased = eraseInstruction(*ResultPlan->Extension); !Erased) + return std::unexpected(Erased.error()); + } + if (auto Erased = eraseInstruction(Instruction); !Erased) + return std::unexpected(Erased.error()); + LeftParts->Artifacts.insert(LeftParts->Artifacts.end(), RightParts->Artifacts.begin(), + RightParts->Artifacts.end()); + eraseDeadArtifacts(LeftParts->Artifacts, Function); + return {}; +} + +[[nodiscard]] bool canLowerNarrowBranchCondition(const MachineInstr &Instruction, + const MachineFunction &Function) { + if (Instruction.opcode() != opcode(GenericOpcode::G_BRCOND) || + Instruction.operandCount() < 2) + return false; + const MachineOperand *ConditionOperand = Instruction.operandAt(0); + const Register *Condition = + ConditionOperand == nullptr ? nullptr : ConditionOperand->getIf<Register>(); + if (Condition == nullptr || !Condition->isVirtual() || + singleRegisterUse(Function, *Condition) != &Instruction) + return false; + const auto ConditionInfo = Function.registerInfo().virtualRegisterInfo(*Condition); + const MachineOperand *Definition = + Function.registerInfo().getUniqueVRegDef(*Condition); + const MachineInstr *Truncation = + Definition == nullptr ? nullptr : Definition->parent(); + if (!ConditionInfo || !ConditionInfo->get().Type || + ConditionInfo->get().Type->scalarBitWidth() != 1 || Truncation == nullptr || + Truncation->opcode() != opcode(GenericOpcode::G_TRUNC) || + Truncation->operandCount() != 2) + return false; + return operandType(Function, *Truncation, 1) + .transform([](LLT Type) { return isScalarGPRType(Type) && Type.scalarBitWidth() == 64; }) + .value_or(false); +} + +[[nodiscard]] Expected<void> +lowerNarrowBranchCondition(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target) { + if (!canLowerNarrowBranchCondition(Instruction, Function)) + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V branch condition is not a narrow truncation"); + const MachineOperand *ConditionOperand = Instruction.operandAt(0); + const Register Condition = *ConditionOperand->getIf<Register>(); + const MachineOperand *Definition = + Function.registerInfo().getUniqueVRegDef(Condition); + MachineInstr *Truncation = + const_cast<MachineInstr *>(Definition->parent()); + const Register Source = *Truncation->operandAt(1)->getIf<Register>(); + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + MachineIRBuilder Builder(Function, Target); + auto One = buildIntegerConstantBefore(Builder, Instruction, 64, 1); + if (!One) + return std::unexpected(One.error()); + auto Masked = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_AND, *Type, + Source, *One); + if (!Masked) + return std::unexpected(Masked.error()); + auto Replaced = Instruction.operandAt(0)->setRegister(*Masked); + if (!Replaced) + return std::unexpected(Replaced.error()); + if (Function.registerInfo().useEmpty(Condition)) + return eraseInstruction(*Truncation); + return {}; +} + +struct NarrowPhiIncoming final { + Register Value; + MachineBasicBlock *Block = nullptr; + MachineInstr *ReusableTruncation = nullptr; +}; + +[[nodiscard]] MachineInstr *firstInstructionAfterLeadingCopies(MachineBasicBlock &Block) { + MachineInstr *Instruction = Block.firstInstruction(); + while (Instruction != nullptr && + Instruction->opcode() == opcode(GenericOpcode::COPY)) + Instruction = Instruction->next(); + return Instruction; +} + +[[nodiscard]] Expected<Register> +buildIncomingConversion(MachineFunction &Function, const TargetDescription &Target, + MachineBasicBlock &Block, MachineInstr *Anchor, + GenericOpcode Generic, LLT Type, Register Source) { + auto Result = Function.registerInfo().createVirtualRegister(Type); + if (!Result) + return std::unexpected(Result.error()); + MachineIRBuilder Builder(Function, Target); + auto Point = Anchor == nullptr ? Builder.setInsertPoint(Block) + : Builder.setInsertPointBefore(*Anchor); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(opcode(Generic)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(*Result); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Source); !Added) + return std::unexpected(Added.error()); + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + return *Result; +} + +[[nodiscard]] Expected<void> lowerNarrowPhi(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) { + const std::optional<LLT> OriginalType = resultType(Function, Instruction); + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + if (!OriginalType || Destination == nullptr || OriginalType->scalarBitWidth() >= 32 || + Instruction.operandCount() < 3 || (Instruction.operandCount() % 2U) == 0) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V narrow PHI has invalid operands"); + auto LegalType = LLT::makeAnyScalar(32); + if (!LegalType) + return std::unexpected(LegalType.error()); + + std::vector<NarrowPhiIncoming> Incoming; + Incoming.reserve((Instruction.operandCount() - 1U) / 2U); + for (std::size_t Index = 1; Index < Instruction.operandCount(); Index += 2) { + const MachineOperand *ValueOperand = Instruction.operandAt(Index); + const Register *Value = + ValueOperand == nullptr ? nullptr : ValueOperand->getIf<Register>(); + const MachineOperand *BlockOperand = Instruction.operandAt(Index + 1U); + const auto *Block = + BlockOperand == nullptr ? nullptr : BlockOperand->getIf<BasicBlockOperand>(); + if (Value == nullptr || Block == nullptr || Block->Block == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V narrow PHI has an invalid incoming edge"); + MachineInstr *Reusable = nullptr; + const MachineOperand *Definition = + Value->isVirtual() ? Function.registerInfo().getUniqueVRegDef(*Value) : nullptr; + MachineInstr *Def = + Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); + if (Def != nullptr && Def->opcode() == opcode(GenericOpcode::G_TRUNC) && + Def->operandCount() == 2 && singleRegisterUse(Function, *Value) == &Instruction && + operandType(Function, *Def, 1) + .transform([](LLT Type) { return Type.scalarBitWidth() >= 32; }) + .value_or(false)) { + Reusable = Def; + } + Incoming.push_back( + NarrowPhiIncoming{.Value = *Value, .Block = Block->Block, + .ReusableTruncation = Reusable}); + } + for (NarrowPhiIncoming &Entry : Incoming) { + if (Entry.ReusableTruncation == nullptr) + continue; + const std::size_t Count = static_cast<std::size_t>(std::ranges::count( + Incoming, Entry.ReusableTruncation, + &NarrowPhiIncoming::ReusableTruncation)); + if (Count != 1) + Entry.ReusableTruncation = nullptr; + } + for (NarrowPhiIncoming &Entry : Incoming) { + if (Entry.ReusableTruncation == nullptr) + continue; + MachineBasicBlock *OriginalBlock = Entry.ReusableTruncation->parent(); + if (OriginalBlock == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V narrow PHI input truncation is detached"); + auto Removed = OriginalBlock->removeInstruction(*Entry.ReusableTruncation); + if (!Removed) + return std::unexpected(Removed.error()); + } + + std::vector<std::pair<MachineBasicBlock *, MachineInstr *>> Anchors; + for (const NarrowPhiIncoming &Entry : Incoming) { + if (std::ranges::find(Anchors, Entry.Block, + &decltype(Anchors)::value_type::first) == Anchors.end()) { + Anchors.emplace_back(Entry.Block, + firstInstructionAfterLeadingCopies(*Entry.Block)); + } + } + std::vector<Register> LegalIncoming; + LegalIncoming.reserve(Incoming.size()); + for (NarrowPhiIncoming &Entry : Incoming) { + const auto AnchorPosition = std::ranges::find( + Anchors, Entry.Block, &decltype(Anchors)::value_type::first); + MachineInstr *Anchor = AnchorPosition->second; + if (Entry.ReusableTruncation != nullptr) { + auto SetType = Function.registerInfo().setType(Entry.Value, *LegalType); + if (!SetType) + return std::unexpected(SetType.error()); + auto Inserted = Anchor == nullptr + ? Entry.Block->appendInstruction(*Entry.ReusableTruncation) + : Entry.Block->insertInstructionBefore( + *Anchor, *Entry.ReusableTruncation); + if (!Inserted) + return std::unexpected(Inserted.error()); + LegalIncoming.push_back(Entry.Value); + continue; + } + const auto ValueInfo = Function.registerInfo().virtualRegisterInfo(Entry.Value); + if (!ValueInfo || !ValueInfo->get().Type) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V narrow PHI input has no scalar type"); + const std::uint32_t Width = ValueInfo->get().Type->scalarBitWidth(); + const GenericOpcode Conversion = Width < 32 ? GenericOpcode::G_ANYEXT + : Width > 32 ? GenericOpcode::G_TRUNC + : GenericOpcode::COPY; + auto Converted = buildIncomingConversion(Function, Target, *Entry.Block, Anchor, + Conversion, *LegalType, Entry.Value); + if (!Converted) + return std::unexpected(Converted.error()); + LegalIncoming.push_back(*Converted); + } + + auto LegalResult = Function.registerInfo().createVirtualRegister(*LegalType); + if (!LegalResult) + return std::unexpected(LegalResult.error()); + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_PHI)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(*LegalResult); !Added) + return std::unexpected(Added.error()); + for (std::size_t Index = 0; Index < Incoming.size(); ++Index) { + if (auto Added = Pending->addUse(LegalIncoming[Index]); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addBasicBlock(*Incoming[Index].Block); !Added) + return std::unexpected(Added.error()); + } + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + auto Replaced = replaceRegisterUses(Function, *Destination, *LegalResult); + if (!Replaced) + return std::unexpected(Replaced.error()); + return eraseInstruction(Instruction); +} + +[[nodiscard]] Expected<void> lowerWidePhi(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) { + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + if (Destination == nullptr || Instruction.operandCount() < 5 || + (Instruction.operandCount() % 2U) == 0) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide PHI has invalid operands"); + + std::vector<WideValueParts> IncomingParts; + std::vector<MachineBasicBlock *> IncomingBlocks; + for (std::size_t Index = 1; Index < Instruction.operandCount(); Index += 2) { + const MachineOperand *ValueOperand = Instruction.operandAt(Index); + const Register *Value = ValueOperand == nullptr ? nullptr : ValueOperand->getIf<Register>(); + const MachineOperand *BlockOperand = Instruction.operandAt(Index + 1U); + const auto *Block = BlockOperand == nullptr ? nullptr : BlockOperand->getIf<BasicBlockOperand>(); + if (Value == nullptr || Block == nullptr || Block->Block == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide PHI has an invalid incoming edge"); + auto Parts = collectWideValueParts(*Value, Function); + if (!Parts) + return std::unexpected(Parts.error()); + if (!IncomingParts.empty() && Parts->Parts.size() != IncomingParts.front().Parts.size()) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V wide PHI incoming values have different limb counts"); + IncomingParts.push_back(std::move(*Parts)); + IncomingBlocks.push_back(Block->Block); + } + const std::size_t PartCount = IncomingParts.front().Parts.size(); + auto ResultPlan = wideResultPlan(*Destination, Function, PartCount); + if (!ResultPlan) + return std::unexpected(ResultPlan.error()); + auto Type = LLT::makeAnyScalar(64); + if (!Type) + return std::unexpected(Type.error()); + MachineIRBuilder Builder(Function, Target); + std::vector<Register> Results; + Results.reserve(PartCount); + for (std::size_t PartIndex = 0; PartIndex < PartCount; ++PartIndex) { + auto Result = Function.registerInfo().createVirtualRegister(*Type); + if (!Result) + return std::unexpected(Result.error()); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_PHI)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(*Result); !Added) + return std::unexpected(Added.error()); + for (std::size_t IncomingIndex = 0; IncomingIndex < IncomingParts.size(); + ++IncomingIndex) { + if (auto Added = Pending->addUse(IncomingParts[IncomingIndex].Parts[PartIndex]); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addBasicBlock(*IncomingBlocks[IncomingIndex]); !Added) + return std::unexpected(Added.error()); + } + auto Built = Pending->build(); + if (!Built) + return std::unexpected(Built.error()); + Results.push_back(*Result); + } + for (std::size_t Index = 0; Index < PartCount; ++Index) { + auto Replaced = replaceRegisterUses(Function, ResultPlan->Outputs[Index], Results[Index]); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + if (auto Erased = eraseInstruction(*ResultPlan->Unmerge); !Erased) + return std::unexpected(Erased.error()); + if (ResultPlan->Extension != nullptr) { + if (auto Erased = eraseInstruction(*ResultPlan->Extension); !Erased) + return std::unexpected(Erased.error()); + } + if (auto Erased = eraseInstruction(Instruction); !Erased) + return std::unexpected(Erased.error()); + std::vector<MachineInstr *> Artifacts; + for (WideValueParts &Parts : IncomingParts) + Artifacts.insert(Artifacts.end(), Parts.Artifacts.begin(), Parts.Artifacts.end()); + eraseDeadArtifacts(Artifacts, Function); + return {}; +} + +[[nodiscard]] bool canFoldWideMergeImmediately(const MachineInstr &Instruction, + const MachineFunction &Function) { + const MachineOperand *LowOperand = Instruction.operandAt(1); + const Register *Low = LowOperand == nullptr ? nullptr : LowOperand->getIf<Register>(); + if (Low == nullptr) + return false; + const auto LowInfo = Function.registerInfo().virtualRegisterInfo(*Low); + if (!LowInfo || !LowInfo->get().Type) + return false; + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Destination = + DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); + if (Destination == nullptr) + return false; + auto &RegisterInfo = const_cast<MachineRegisterInfo &>(Function.registerInfo()); + for (MachineOperand &Use : RegisterInfo.useOperands(*Destination)) { + MachineInstr *Trunc = Use.parent(); + if (Trunc == nullptr || Trunc->opcode() != opcode(GenericOpcode::G_TRUNC) || + Trunc->operandAt(1) != &Use) + return false; + const std::optional<LLT> Result = resultType(Function, *Trunc); + if (!Result || Result->scalarBitWidth() > LowInfo->get().Type->scalarBitWidth()) + return false; + } + return true; +} + class RISCVLegalizerInfo final : public TargetLegalizerInfo { public: [[nodiscard]] Expected<LegalizationAction> classify(const MachineInstr &Instruction, @@ -251,17 +2474,26 @@ public: return LegalizationAction::Legal; case GenericOpcode::G_BRCOND: { const std::optional<LLT> Condition = operandType(Function, Instruction, 0); - return Condition && isI1Type(*Condition) ? LegalizationAction::Legal - : LegalizationAction::Unsupported; + if (!Condition || !isScalarGPRType(*Condition)) + return LegalizationAction::Unsupported; + return canLowerNarrowBranchCondition(Instruction, Function) + ? LegalizationAction::Custom + : LegalizationAction::Legal; } case GenericOpcode::G_PHI: { - if (!Type || (!isSupportedIntegerType(*Type) && !isRVFloatType(*Type))) + if (!Type) return LegalizationAction::Unsupported; for (std::size_t Index = 1; Index < Instruction.operandCount(); Index += 2) { const std::optional<LLT> Incoming = operandType(Function, Instruction, Index); if (!Incoming || *Incoming != *Type) return LegalizationAction::Unsupported; } + if ((Type->isInteger() || Type->isAnyScalar()) && Type->scalarBitWidth() > 64) + return LegalizationAction::Custom; + if ((Type->isInteger() || Type->isAnyScalar()) && Type->scalarBitWidth() < 32) + return LegalizationAction::Custom; + if (!isSupportedIntegerType(*Type) && !isRVFloatType(*Type)) + return LegalizationAction::Unsupported; return LegalizationAction::Legal; } case GenericOpcode::G_IMPLICIT_DEF: @@ -436,18 +2668,75 @@ public: case GenericOpcode::G_SEXT: case GenericOpcode::G_ZEXT: { const std::optional<LLT> Source = operandType(Function, Instruction, 1); - return Type && Source && isScalarIntegerExtension(*Type, *Source) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; + if (!Type || !Source) + return LegalizationAction::Unsupported; + if (!isScalarIntegerExtension(*Type, *Source)) { + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const Register *Destination = DestinationOperand == nullptr + ? nullptr + : DestinationOperand->getIf<Register>(); + MachineInstr *OnlyUse = Destination == nullptr + ? nullptr + : singleUseInstruction(Function, *Destination); + return (Type->isInteger() || Type->isAnyScalar()) && + (Source->isInteger() || Source->isAnyScalar()) && + Type->scalarBitWidth() > 64 && + Source->scalarBitWidth() < Type->scalarBitWidth() && + OnlyUse != nullptr && + OnlyUse->opcode() == opcode(GenericOpcode::G_UNMERGE_VALUES) + ? LegalizationAction::Legal + : LegalizationAction::Unsupported; + } + const MachineOperand *SourceOperand = Instruction.operandAt(1); + const Register *SourceRegister = + SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); + const MachineOperand *SourceDefinition = + SourceRegister == nullptr || !SourceRegister->isVirtual() + ? nullptr + : Function.registerInfo().getUniqueVRegDef(*SourceRegister); + const MachineInstr *SourceDef = + SourceDefinition == nullptr ? nullptr : SourceDefinition->parent(); + if (SourceDef != nullptr && SourceDef->opcode() == opcode(GenericOpcode::G_CONSTANT)) + return LegalizationAction::Custom; + if (Generic == GenericOpcode::G_ANYEXT && + narrowAnyExtBinaryPlan(Instruction, Function).has_value()) + return LegalizationAction::Custom; + return LegalizationAction::Legal; } case GenericOpcode::G_TRUNC: { const std::optional<LLT> Source = operandType(Function, Instruction, 1); + if (Type && Source && (Type->isInteger() || Type->isAnyScalar()) && + (Source->isInteger() || Source->isAnyScalar()) && Type->scalarBitWidth() > 64 && + Source->scalarBitWidth() > Type->scalarBitWidth()) + return LegalizationAction::Legal; return Type && Source && isSupportedIntegerScalar(*Type) && isSupportedIntegerScalar(*Source) && Type->scalarBitWidth() != 0 && Source->scalarBitWidth() >= Type->scalarBitWidth() ? LegalizationAction::Legal : LegalizationAction::Unsupported; } + case GenericOpcode::G_MERGE_VALUES: + case GenericOpcode::G_UNMERGE_VALUES: { + if (Instruction.operandCount() < 3) + return LegalizationAction::Unsupported; + const std::size_t SmallTypeIndex = + Generic == GenericOpcode::G_MERGE_VALUES ? 1 : 0; + const std::size_t BigTypeIndex = Generic == GenericOpcode::G_MERGE_VALUES + ? 0 + : Instruction.operandCount() - 1U; + const std::optional<LLT> SmallType = operandType(Function, Instruction, SmallTypeIndex); + const std::optional<LLT> BigType = operandType(Function, Instruction, BigTypeIndex); + if (SmallType && BigType && Generic == GenericOpcode::G_MERGE_VALUES && + BigType->scalarBitWidth() > 64 && + !canFoldWideMergeImmediately(Instruction, Function)) + return LegalizationAction::Legal; + return SmallType && BigType && + (SmallType->isInteger() || SmallType->isAnyScalar()) && + (BigType->isInteger() || BigType->isAnyScalar()) && + SmallType->scalarBitWidth() != 0 && BigType->scalarBitWidth() != 0 + ? LegalizationAction::Custom + : LegalizationAction::Unsupported; + } case GenericOpcode::G_SEXT_INREG: { const std::optional<LLT> Source = operandType(Function, Instruction, 1); const MachineOperand *WidthOperand = Instruction.operandAt(2); @@ -467,7 +2756,7 @@ public: Predicate == nullptr ? nullptr : Predicate->getIf<PredicateOperand>(); const std::optional<LLT> Left = operandType(Function, Instruction, 2); const std::optional<LLT> Right = operandType(Function, Instruction, 3); - return Type && isI1Type(*Type) && IntegerPredicate != nullptr && + return Type && isScalarGPRType(*Type) && IntegerPredicate != nullptr && IntegerPredicate->integerPredicate() != nullptr && Left && Right && *Left == *Right && isSupportedIntegerType(*Left) ? LegalizationAction::Legal @@ -523,7 +2812,7 @@ public: const std::optional<LLT> Condition = operandType(Function, Instruction, 1); const std::optional<LLT> TrueValue = operandType(Function, Instruction, 2); const std::optional<LLT> FalseValue = operandType(Function, Instruction, 3); - return Type && Condition && TrueValue && FalseValue && isI1Type(*Condition) && + return Type && Condition && TrueValue && FalseValue && isScalarGPRType(*Condition) && *TrueValue == *Type && *FalseValue == *Type && (isSupportedIntegerType(*Type) || isRVFloatType(*Type)) ? LegalizationAction::Legal @@ -637,6 +2926,26 @@ public: ? LegalizationAction::Legal : LegalizationAction::Unsupported; } + case GenericOpcode::G_CONSTANT_POOL: { + const MachineOperand *PoolOperand = Instruction.operandAt(1); + const auto *Pool = PoolOperand == nullptr ? nullptr : PoolOperand->getIf<ConstantPoolOperand>(); + return Type && Type->isPointer() && Type->addressSpace() == 0 && + Type->scalarBitWidth() == 64 && Pool != nullptr && + Function.constantPool().entry(Pool->Index) != nullptr + ? LegalizationAction::Legal + : LegalizationAction::Unsupported; + } + case GenericOpcode::G_GLOBAL_VALUE: { + const MachineOperand *GlobalOperand = Instruction.operandAt(1); + const auto *Global = GlobalOperand == nullptr + ? nullptr + : GlobalOperand->getIf<GlobalSymbolOperand>(); + return Type && Type->isPointer() && Type->addressSpace() == 0 && + Type->scalarBitWidth() == 64 && Global != nullptr && + !Global->Name.empty() + ? LegalizationAction::Legal + : LegalizationAction::Unsupported; + } case GenericOpcode::G_BRINDIRECT: return operandType(Function, Instruction, 0) .transform([](LLT Value) { @@ -655,11 +2964,21 @@ public: : LegalizationAction::Unsupported; case GenericOpcode::G_CONSTANT: { const MachineOperand *Immediate = Instruction.operandAt(1); - return Type && isSupportedIntegerType(*Type) && Immediate != nullptr && - (Immediate->kind() == MachineOperand::Kind::Immediate || - Immediate->kind() == MachineOperand::Kind::IntegerImmediate) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; + if (!Type || Immediate == nullptr || + (Immediate->kind() != MachineOperand::Kind::Immediate && + Immediate->kind() != MachineOperand::Kind::IntegerImmediate)) + return LegalizationAction::Unsupported; + if (Type->scalarBitWidth() > 64) + return wideConstantUnmergePlan(Instruction, Function).has_value() + ? LegalizationAction::Custom + : LegalizationAction::Unsupported; + if ((Type->isInteger() || Type->isAnyScalar()) && Type->scalarBitWidth() == 64) { + auto Value = signedImmediate(*Immediate); + if (Value && shouldUseRV64IntegerConstantPool(*Value)) + return LegalizationAction::Custom; + } + return isSupportedIntegerType(*Type) ? LegalizationAction::Legal + : LegalizationAction::Unsupported; } case GenericOpcode::G_ADD: case GenericOpcode::G_SUB: @@ -676,10 +2995,18 @@ public: case GenericOpcode::G_ASHR: { const std::optional<LLT> Left = operandType(Function, Instruction, 1); const std::optional<LLT> Right = operandType(Function, Instruction, 2); - return Type && Left && Right && isSupportedIntegerScalar(*Type) && *Left == *Type && - *Right == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; + if (!Type || !Left || !Right || *Left != *Type || *Right != *Type || + (!Type->isInteger() && !Type->isAnyScalar())) + return LegalizationAction::Unsupported; + if (Type->scalarBitWidth() > 64) { + return Generic == GenericOpcode::G_ADD || Generic == GenericOpcode::G_SUB || + Generic == GenericOpcode::G_AND || Generic == GenericOpcode::G_OR || + Generic == GenericOpcode::G_XOR || Generic == GenericOpcode::G_SHL + ? LegalizationAction::Custom + : LegalizationAction::Unsupported; + } + return isSupportedIntegerScalar(*Type) ? LegalizationAction::Legal + : LegalizationAction::Unsupported; } case GenericOpcode::G_PTR_ADD: { const std::optional<LLT> Pointer = operandType(Function, Instruction, 1); @@ -713,8 +3040,60 @@ public: "RISC-V legalizer received an invalid generic opcode value"); } - [[nodiscard]] Expected<void> lower(MachineInstr &, MachineFunction &, const TargetDescription &, - LegalizationAction) const override { + [[nodiscard]] Expected<void> lower(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target, + LegalizationAction Action) const override { + if (Action == LegalizationAction::Custom && + resultType(Function, Instruction) + .transform([](LLT Type) { return Type.scalarBitWidth() > 64; }) + .value_or(false)) { + const Opcode Value = Instruction.opcode(); + if (Value == opcode(GenericOpcode::G_ADD) || Value == opcode(GenericOpcode::G_SUB) || + Value == opcode(GenericOpcode::G_AND) || Value == opcode(GenericOpcode::G_OR) || + Value == opcode(GenericOpcode::G_XOR)) { + return lowerWideBinary(Instruction, Function, Target, + static_cast<GenericOpcode>(Value.value())); + } + if (Value == opcode(GenericOpcode::G_SHL)) + return lowerWideShiftLeft(Instruction, Function, Target); + if (Value == opcode(GenericOpcode::G_PHI)) + return lowerWidePhi(Instruction, Function, Target); + } + if (Action == LegalizationAction::Custom && + Instruction.opcode() == opcode(GenericOpcode::G_MERGE_VALUES)) + return lowerMergeValues(Instruction, Function, Target); + if (Action == LegalizationAction::Custom && + Instruction.opcode() == opcode(GenericOpcode::G_UNMERGE_VALUES)) + return lowerUnmergeValues(Instruction, Function, Target); + if (Action == LegalizationAction::Custom && + Instruction.opcode() == opcode(GenericOpcode::G_BRCOND)) + return lowerNarrowBranchCondition(Instruction, Function, Target); + if (Action == LegalizationAction::Custom && + Instruction.opcode() == opcode(GenericOpcode::G_PHI)) + return lowerNarrowPhi(Instruction, Function, Target); + if (Action == LegalizationAction::Custom && + (Instruction.opcode() == opcode(GenericOpcode::G_ANYEXT) || + Instruction.opcode() == opcode(GenericOpcode::G_SEXT) || + Instruction.opcode() == opcode(GenericOpcode::G_ZEXT))) { + if (Instruction.opcode() == opcode(GenericOpcode::G_ANYEXT) && + narrowAnyExtBinaryPlan(Instruction, Function).has_value()) + return foldNarrowAnyExtBinary(Instruction, Function, Target); + return foldConstantExtension( + Instruction, Function, + static_cast<GenericOpcode>(Instruction.opcode().value())); + } + if (Action == LegalizationAction::Custom && + Instruction.opcode() == opcode(GenericOpcode::G_CONSTANT)) { + const MachineOperand *Immediate = Instruction.operandAt(1); + const std::optional<LLT> Type = resultType(Function, Instruction); + if (Immediate != nullptr && Type && (Type->isInteger() || Type->isAnyScalar()) && + Type->scalarBitWidth() == 64) { + auto Value = signedImmediate(*Immediate); + if (Value && shouldUseRV64IntegerConstantPool(*Value)) + return lowerConstantToPool(Instruction, Function, Target); + } + return foldWideConstant(Instruction, Function, Target); + } return makeUnexpected(ErrorCode::Unsupported, "RISC-V legalizer has no lowering rule for this instruction"); } @@ -786,9 +3165,25 @@ public: class RISCVInstructionSelector final : public TargetInstructionSelector { public: + [[nodiscard]] Expected<void> + constrainSelectedInstruction(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target) const override { + return registerBankInfo().constrainInstruction(Instruction, Function, Target); + } + [[nodiscard]] Expected<void> canSelect(const MachineInstr &Instruction, const MachineFunction &Function, const TargetDescription &Target) const override { + if (Instruction.opcode() == targetOpcode(InstructionOpcode::G_SLLW)) { + const std::optional<LLT> Type = resultType(Function, Instruction); + const std::optional<LLT> Left = operandType(Function, Instruction, 1); + const std::optional<LLT> Right = operandType(Function, Instruction, 2); + return Type && Left && Right && isScalarGPRType(*Type) && + Type->scalarBitWidth() == 64 && *Left == *Type && *Right == *Type + ? Expected<void>{} + : makeUnexpected(ErrorCode::Unsupported, + "RISC-V G_SLLW requires three 64-bit GPR operands"); + } if (!isGenericOpcode(Instruction.opcode())) return {}; const GenericOpcode Generic = static_cast<GenericOpcode>(Instruction.opcode().value()); @@ -938,6 +3333,8 @@ public: auto Match = canSelect(Instruction, Function, Target); if (!Match) return std::unexpected(Match.error()); + if (Instruction.opcode() == targetOpcode(InstructionOpcode::G_SLLW)) + return Instruction.setOpcode(targetOpcode(InstructionOpcode::SLLW)); const GenericOpcode Generic = static_cast<GenericOpcode>(Instruction.opcode().value()); const std::optional<LLT> Type = resultType(Function, Instruction); @@ -2974,6 +5371,7 @@ private: } const Register Destination = *DestinationValue; const std::int64_t Value = *ImmediateValue; + const std::uint32_t ImmediateTargetFlags = ImmediateOperand->targetFlags(); const bool IsWord = Type->isInteger() && Type->scalarBitWidth() <= 32; const InstructionOpcode AddImmediate = IsWord ? InstructionOpcode::ADDIW : InstructionOpcode::ADDI; @@ -2986,6 +5384,13 @@ private: auto Source = insertPhysicalRegister(Instruction, 1, *Zero, RegisterOperandFlag::Use); if (!Source) return std::unexpected(Source.error()); + auto EraseImmediate = Instruction.eraseOperand(2); + if (!EraseImmediate) + return std::unexpected(EraseImmediate.error()); + auto NormalizeImmediate = Instruction.insertOperand( + 2, MachineOperand::immediate(Value, ImmediateTargetFlags)); + if (!NormalizeImmediate) + return std::unexpected(NormalizeImmediate.error()); return Instruction.setOpcode(targetOpcode(AddImmediate)); } diff --git a/backend/lib/Target/RISCV/RISCVFunctionInfo.cpp b/backend/lib/Target/RISCV/RISCVFunctionInfo.cpp new file mode 100644 index 0000000..f25bb8c --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVFunctionInfo.cpp @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/Target/RISCV/RISCVFunctionInfo.h" + +#include "rcc/CodeGen/TargetMIRTextInfo.h" + +#include <bit> +#include <charconv> +#include <cstddef> +#include <cstdint> +#include <span> +#include <string> +#include <string_view> +#include <system_error> +#include <utility> +#include <vector> + +namespace rcc::riscv { +namespace { + +[[nodiscard]] Expected<std::int32_t> parseSignedField(const MIRTargetNode &Node, + std::string_view Name) { + if (Node.Kind != MIRTargetNodeKind::Scalar) + return makeUnexpected(ErrorCode::ParseError, + "RISC-V machineFunctionInfo field '" + std::string(Name) + + "' must be a scalar"); + std::int32_t Value = 0; + const auto Result = + std::from_chars(Node.Scalar.data(), Node.Scalar.data() + Node.Scalar.size(), Value, 10); + if (Result.ec != std::errc{} || Result.ptr != Node.Scalar.data() + Node.Scalar.size()) { + return makeUnexpected(ErrorCode::ParseError, + "RISC-V machineFunctionInfo field '" + std::string(Name) + + "' must be a 32-bit signed integer"); + } + return Value; +} + +[[nodiscard]] Expected<TargetFunctionState> parseFunctionInfo(const MIRTargetNode &Node) { + if (Node.Kind != MIRTargetNodeKind::Mapping) + return makeUnexpected(ErrorCode::ParseError, + "RISC-V machineFunctionInfo must be a mapping"); + + std::int32_t VarArgsFrameIndex = 0; + std::int32_t VarArgsSaveSize = 0; + for (const MIRTargetMappingEntry &Entry : Node.Mapping) { + if (Entry.Key == "varArgsFrameIndex") { + auto Value = parseSignedField(Entry.Value, Entry.Key); + if (!Value) + return std::unexpected(Value.error()); + VarArgsFrameIndex = *Value; + continue; + } + if (Entry.Key == "varArgsSaveSize") { + auto Value = parseSignedField(Entry.Value, Entry.Key); + if (!Value) + return std::unexpected(Value.error()); + VarArgsSaveSize = *Value; + continue; + } + return makeUnexpected(ErrorCode::ParseError, + "unknown RISC-V machineFunctionInfo field '" + Entry.Key + "'"); + } + return TargetFunctionState::create( + RISCVFunctionInfo(VarArgsFrameIndex, VarArgsSaveSize)); +} + +[[nodiscard]] Expected<MIRTargetNode> +printFunctionInfo(const TargetFunctionState &State) { + const auto *Info = State.getIf<RISCVFunctionInfo>(); + if (Info == nullptr) + return makeUnexpected(ErrorCode::InvalidArgument, + "target function state is not RISC-V function information"); + std::vector<MIRTargetMappingEntry> Entries; + Entries.push_back({"varArgsFrameIndex", + MIRTargetNode::scalar(std::to_string(Info->varArgsFrameIndex()))}); + Entries.push_back({"varArgsSaveSize", + MIRTargetNode::scalar(std::to_string(Info->varArgsSaveSize()))}); + return MIRTargetNode::mapping(std::move(Entries)); +} + +void appendWord(std::vector<std::byte> &Payload, std::int32_t Value) { + const std::uint32_t Bits = std::bit_cast<std::uint32_t>(Value); + for (std::uint32_t Shift = 0; Shift != 32; Shift += 8) + Payload.push_back(static_cast<std::byte>((Bits >> Shift) & 0xFFU)); +} + +[[nodiscard]] std::int32_t readWord(std::span<const std::byte> Payload, + std::size_t Offset) noexcept { + std::uint32_t Bits = 0; + for (std::uint32_t Byte = 0; Byte != 4; ++Byte) { + Bits |= std::to_integer<std::uint32_t>(Payload[Offset + Byte]) << (Byte * 8U); + } + return std::bit_cast<std::int32_t>(Bits); +} + +} // namespace + +Expected<void> RISCVFunctionInfo::verify() const { + return {}; +} + +Expected<RISCVFunctionInfo> +RISCVFunctionInfo::clone(const MachineFunctionCloneContext &) const { + return RISCVFunctionInfo(VarArgsFrameIndex, VarArgsSaveSize); +} + +std::string RISCVFunctionInfo::print() const { + return "riscv-varargs:" + std::to_string(VarArgsFrameIndex) + ":" + + std::to_string(VarArgsSaveSize); +} + +Expected<std::vector<std::byte>> RISCVFunctionInfo::encode() const { + std::vector<std::byte> Payload; + Payload.reserve(8); + appendWord(Payload, VarArgsFrameIndex); + appendWord(Payload, VarArgsSaveSize); + return Payload; +} + +Expected<RISCVFunctionInfo> +RISCVFunctionInfo::decode(std::uint32_t Version, std::span<const std::byte> Payload) { + if (Version != schemaVersion()) + return makeUnexpected(ErrorCode::ParseError, + "unsupported RISC-V function-info schema version"); + if (Payload.size() != 8) + return makeUnexpected(ErrorCode::ParseError, + "RISC-V function-info payload must contain eight bytes"); + return RISCVFunctionInfo(readWord(Payload, 0), readWord(Payload, 4)); +} + +void configureFunctionInfoMIRSchema(TargetMIRTextInfoDesc &Description) { + Description.FunctionStateType = RISCVFunctionInfo::stableTypeID(); + Description.ParseFunctionState = parseFunctionInfo; + Description.PrintFunctionState = printFunctionInfo; +} + +} // namespace rcc::riscv diff --git a/backend/lib/Target/RISCV/RISCVImmediateMaterialization.cpp b/backend/lib/Target/RISCV/RISCVImmediateMaterialization.cpp new file mode 100644 index 0000000..56bce25 --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVImmediateMaterialization.cpp @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "RISCVImmediateMaterialization.h" + +#include <algorithm> +#include <bit> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <optional> + +namespace rcc::riscv { +namespace { + +[[nodiscard]] constexpr bool fitsSigned(std::int64_t Value, std::uint32_t Bits) noexcept { + const std::int64_t Minimum = -(std::int64_t{1} << (Bits - 1U)); + const std::int64_t Maximum = (std::int64_t{1} << (Bits - 1U)) - 1; + return Value >= Minimum && Value <= Maximum; +} + +[[nodiscard]] constexpr std::int64_t signExtend(std::uint64_t Value, + std::uint32_t Bits) noexcept { + const std::uint64_t Mask = (std::uint64_t{1} << Bits) - 1U; + const std::uint64_t Sign = std::uint64_t{1} << (Bits - 1U); + Value &= Mask; + if ((Value & Sign) != 0) + Value |= ~Mask; + return std::bit_cast<std::int64_t>(Value); +} + +[[nodiscard]] std::size_t baseBuildCost(std::int64_t Value) noexcept { + const std::uint64_t Bits = static_cast<std::uint64_t>(Value); + if (fitsSigned(Value, 32)) { + const std::uint64_t High20 = ((Bits + 0x800U) >> 12U) & 0xFFFFFU; + const std::int64_t Low12 = signExtend(Bits, 12); + return static_cast<std::size_t>(High20 != 0) + + static_cast<std::size_t>(Low12 != 0 || High20 == 0); + } + + const std::int64_t Low12 = signExtend(Bits, 12); + const std::uint64_t WithoutLow12 = Bits - static_cast<std::uint64_t>(Low12); + std::int64_t Remaining = std::bit_cast<std::int64_t>(WithoutLow12); + std::uint32_t Shift = 0; + if (!fitsSigned(Remaining, 32)) { + Shift = static_cast<std::uint32_t>(std::countr_zero(WithoutLow12)); + Remaining >>= Shift; + + if (Shift > 12 && !fitsSigned(Remaining, 12)) { + const std::int64_t WithLUILowBits = + std::bit_cast<std::int64_t>(static_cast<std::uint64_t>(Remaining) << 12U); + if (fitsSigned(WithLUILowBits, 32)) { + Shift -= 12; + Remaining = WithLUILowBits; + } + } + } + + return baseBuildCost(Remaining) + static_cast<std::size_t>(Shift != 0) + + static_cast<std::size_t>(Low12 != 0); +} + +[[nodiscard]] std::optional<std::size_t> +leadingZeroBuildCost(std::uint64_t Value) noexcept { + if (Value == 0 || (Value & (std::uint64_t{1} << 63U)) != 0) + return std::nullopt; + + const std::uint32_t LeadingZeros = + static_cast<std::uint32_t>(std::countl_zero(Value)); + const std::uint64_t Shifted = Value << LeadingZeros; + const std::uint64_t LowMask = (std::uint64_t{1} << LeadingZeros) - 1U; + std::optional<std::size_t> Best; + for (const std::uint64_t Candidate : {Shifted | LowMask, Shifted}) { + const std::size_t Cost = baseBuildCost(std::bit_cast<std::int64_t>(Candidate)); + if (Cost >= 8) + continue; + const std::size_t WithShift = Cost + 1U; + Best = Best ? std::min(*Best, WithShift) : WithShift; + } + return Best; +} + +[[nodiscard]] std::optional<std::size_t> +twoRegisterBuildCost(std::int64_t Value) noexcept { + const std::uint64_t Bits = static_cast<std::uint64_t>(Value); + const std::int64_t Low = signExtend(Bits, 32); + if (Low == 0) + return std::nullopt; + + const std::uint64_t HighContribution = Bits - static_cast<std::uint64_t>(Low); + if (HighContribution == 0) + return std::nullopt; + const std::uint32_t LowZeros = + static_cast<std::uint32_t>(std::countr_zero(static_cast<std::uint64_t>(Low))); + const std::uint32_t HighZeros = + static_cast<std::uint32_t>(std::countr_zero(HighContribution)); + if (LowZeros >= 32 || HighZeros < 32 || HighZeros < LowZeros) + return std::nullopt; + const std::uint32_t Shift = HighZeros - LowZeros; + if (Shift >= 64 || HighContribution != (static_cast<std::uint64_t>(Low) << Shift)) + return std::nullopt; + return estimateRV64IntegerBuildCost(Low) + 2U; +} + +} // namespace + +std::size_t estimateRV64IntegerBuildCost(std::int64_t Value) noexcept { + const std::uint64_t Bits = static_cast<std::uint64_t>(Value); + std::size_t Best = baseBuildCost(Value); + + if ((Bits & 0xFFFU) != 0 && (Bits & 1U) == 0 && Best >= 2) { + const std::uint32_t TrailingZeros = + static_cast<std::uint32_t>(std::countr_zero(Bits)); + const std::int64_t Shifted = Value >> TrailingZeros; + const std::size_t Candidate = baseBuildCost(Shifted) + 1U; + if (Candidate < Best || fitsSigned(Shifted, 6)) + Best = Candidate; + } + if (Best <= 2) + return Best; + + if ((Bits & 0xFFFU) != 0 && (Bits & 0x1800U) == 0x1000U) { + const std::int64_t LastAdd = + -static_cast<std::int64_t>(0x800U - (Bits & 0xFFFU)); + const std::uint64_t AdjustedBits = Bits - static_cast<std::uint64_t>(LastAdd); + Best = std::min(Best, baseBuildCost(std::bit_cast<std::int64_t>(AdjustedBits)) + 1U); + } + + if (Value > 0 && Best > 2) { + if (const auto Candidate = leadingZeroBuildCost(Bits); Candidate && *Candidate < Best) + Best = *Candidate; + } else if (Value < 0 && Best > 3) { + if (const auto Candidate = leadingZeroBuildCost(~Bits); + Candidate && *Candidate + 1U < Best) { + Best = *Candidate + 1U; + } + } + return Best; +} + +bool shouldUseRV64IntegerConstantPool(std::int64_t Value, + std::size_t MaximumBuildCost) noexcept { + if (fitsSigned(Value, 32) || estimateRV64IntegerBuildCost(Value) <= MaximumBuildCost) + return false; + const auto TwoRegisterCost = twoRegisterBuildCost(Value); + return !TwoRegisterCost || *TwoRegisterCost > MaximumBuildCost; +} + +} // namespace rcc::riscv diff --git a/backend/lib/Target/RISCV/RISCVImmediateMaterialization.h b/backend/lib/Target/RISCV/RISCVImmediateMaterialization.h new file mode 100644 index 0000000..99d3876 --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVImmediateMaterialization.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_LIB_TARGET_RISCV_RISCVIMMEDIATEMATERIALIZATION_H +#define RCC_LIB_TARGET_RISCV_RISCVIMMEDIATEMATERIALIZATION_H + +#include <cstddef> +#include <cstdint> + +namespace rcc::riscv { + +inline constexpr std::size_t DefaultRV64IntegerBuildCost = 5; + +[[nodiscard]] std::size_t estimateRV64IntegerBuildCost(std::int64_t Value) noexcept; + +[[nodiscard]] bool +shouldUseRV64IntegerConstantPool( + std::int64_t Value, + std::size_t MaximumBuildCost = DefaultRV64IntegerBuildCost) noexcept; + +} // namespace rcc::riscv + +#endif // RCC_LIB_TARGET_RISCV_RISCVIMMEDIATEMATERIALIZATION_H diff --git a/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp b/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp index ed73794..8a309e0 100644 --- a/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp +++ b/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp @@ -473,6 +473,44 @@ copyRegisters(const MachineInstr &Instruction) { return true; } +[[nodiscard]] Expected<bool> expandBranch(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target) { + const MachineOperand *TargetOperand = Instruction.operandAt(0); + const BasicBlockOperand *TargetBlock = + TargetOperand == nullptr ? nullptr : TargetOperand->getIf<BasicBlockOperand>(); + if (TargetBlock == nullptr || TargetBlock->Block == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V PseudoBR has no basic-block target"); + } + + auto Zero = physical(PhysicalRegisterID::X0); + if (!Zero) + return std::unexpected(Zero.error()); + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(targetOpcode(InstructionOpcode::JAL)); + if (!Pending) + return std::unexpected(Pending.error()); + auto Def = Pending->addDef(Register::fromPhysical(*Zero)); + if (!Def) + return std::unexpected(Def.error()); + auto Destination = Pending->addBasicBlock(*TargetBlock->Block); + if (!Destination) + return std::unexpected(Destination.error()); + auto Replacement = Pending->build(); + if (!Replacement) + return std::unexpected(Replacement.error()); + auto Decoration = copyCommonDecoration(Instruction, **Replacement, true, true, true); + if (!Decoration) + return std::unexpected(Decoration.error()); + auto Removed = removeOldInstruction(Instruction); + if (!Removed) + return std::unexpected(Removed.error()); + return true; +} + class RISCVPseudoExpansionInfo final : public TargetPseudoExpansionInfo { public: [[nodiscard]] Expected<bool> preparePostRAPseudo(MachineInstr &Instruction, @@ -498,6 +536,8 @@ public: return expandCall(Instruction, Function, Target, true); if (Instruction.opcode() == targetOpcode(InstructionOpcode::PseudoRET)) return expandReturn(Instruction, Function, Target); + if (Instruction.opcode() == targetOpcode(InstructionOpcode::PseudoBR)) + return expandBranch(Instruction, Function, Target); return false; } }; diff --git a/backend/lib/Target/RISCV/RISCVTarget.cpp b/backend/lib/Target/RISCV/RISCVTarget.cpp index 8b3741a..aabce94 100644 --- a/backend/lib/Target/RISCV/RISCVTarget.cpp +++ b/backend/lib/Target/RISCV/RISCVTarget.cpp @@ -3,6 +3,7 @@ #include "rcc/Target/RISCV/RISCVTarget.h" #include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/Target/RISCV/RISCVFunctionInfo.h" #include <array> #include <cassert> @@ -94,6 +95,7 @@ Expected<TargetMIRTextInfo> createMIRTextInfo() { return std::unexpected(Registers.error()); TargetMIRTextInfoDesc Description; appendGeneratedMIRTextInfo(Description); + configureFunctionInfoMIRSchema(Description); return TargetMIRTextInfo::create(std::move(Description), *Registers); } @@ -114,6 +116,7 @@ Expected<TargetDescription> createTargetDescription() { TargetMIRTextInfoDesc MIRText; appendGeneratedMIRTextInfo(MIRText); + configureFunctionInfoMIRSchema(MIRText); return TargetDescription::create(std::move(*Instructions), std::move(*Registers), std::move(MIRText)); } @@ -121,6 +124,7 @@ Expected<TargetDescription> createTargetDescription() { std::shared_ptr<const RegisterMask> createCallPreservedMask() { TargetMIRTextInfoDesc Description; appendGeneratedMIRTextInfo(Description); + configureFunctionInfoMIRSchema(Description); assert(!Description.RegisterMasks.empty()); return Description.RegisterMasks.front().Mask; } |
