diff options
Diffstat (limited to 'backend/lib')
5 files changed, 480 insertions, 64 deletions
diff --git a/backend/lib/CodeGen/GlobalISel/RegBankSelect.cpp b/backend/lib/CodeGen/GlobalISel/RegBankSelect.cpp index 22c80f7..ef29333 100644 --- a/backend/lib/CodeGen/GlobalISel/RegBankSelect.cpp +++ b/backend/lib/CodeGen/GlobalISel/RegBankSelect.cpp @@ -2,9 +2,10 @@ #include "rcc/CodeGen/GlobalISel/RegBankSelect.h" -#include "rcc/CodeGen/LiveVariables.h" +#include "rcc/CodeGen/GenericOpcodes.h" #include "rcc/CodeGen/MachineBasicBlock.h" #include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineIRBuilder.h" #include "rcc/CodeGen/MachineInstr.h" #include "rcc/CodeGen/MachineRegisterInfo.h" #include "rcc/CodeGen/TargetDescription.h" @@ -12,6 +13,7 @@ #include <cstdint> #include <optional> #include <string> +#include <unordered_set> #include <utility> #include <vector> @@ -95,17 +97,187 @@ registerConfigurationFingerprint(const MachineRegisterInfo &RegInfo) noexcept { return Error(Code, std::move(Message)); } -[[nodiscard]] Expected<void> repairTrackedLiveness(MachineFunction &Function, - const TargetDescription &Target, - MachinePassResult &Result) { - if (!Result.Changed || !Function.properties().has(MachineFunctionProperty::TracksLiveness)) { - return {}; - } - auto Rebuilt = rebuildMachineLiveness(Function, Target); - if (!Rebuilt) - return std::unexpected(Rebuilt.error()); - if (*Rebuilt) - Result.Changes.add(MachinePassChange::Operands); +struct PlannedRegisterBank final { + Register Reg; + RegisterBankID Bank; + bool IsConstrained = false; +}; + +struct MappingMutation final { + MachineOperand *Operand = nullptr; + Register Original; + Register Fresh; + MachineInstr *Copy = nullptr; +}; + +[[nodiscard]] Expected<MachineInstr *> buildBankCopy(MachineFunction &Function, + const TargetDescription &Target, + MachineInstr &Instruction, bool InsertBefore, + Register Destination, Register Source) { + MachineIRBuilder Builder(Function, Target); + auto Point = InsertBefore ? Builder.setInsertPointBefore(Instruction) + : Builder.setInsertPointAfter(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(opcode(GenericOpcode::COPY)); + if (!Pending) + return std::unexpected(Pending.error()); + auto DestinationOperand = Pending->addDef(Destination); + if (!DestinationOperand) + return std::unexpected(DestinationOperand.error()); + auto SourceOperand = Pending->addUse(Source); + if (!SourceOperand) + return std::unexpected(SourceOperand.error()); + if (Instruction.debugLocation()) { + auto Debug = Pending->setDebugLocation(*Instruction.debugLocation()); + if (!Debug) + return std::unexpected(Debug.error()); + } + return Pending->build(); +} + +[[nodiscard]] Expected<void> repairMappedOperand(MachineFunction &Function, + const TargetDescription &Target, + MachineInstr &Instruction, MachineOperand &Operand, + RegisterBankID RequiredBank, + std::vector<MappingMutation> &Mutations) { + const Register *Original = Operand.getIf<Register>(); + if (Original == nullptr || !Original->isVirtual() || Operand.isDef() == Operand.isUse()) { + return makeUnexpected(ErrorCode::VerificationError, + "register-bank repair requires a virtual def or use operand"); + } + const Register OriginalReg = *Original; + auto OriginalInfo = Function.registerInfo().virtualRegisterInfo(OriginalReg); + if (!OriginalInfo) + return std::unexpected(OriginalInfo.error()); + if (!OriginalInfo->get().Type) { + return makeUnexpected(ErrorCode::VerificationError, + "register-bank repair requires a virtual-register type"); + } + if (Operand.subReg().value() != 0) { + return makeUnexpected(ErrorCode::Unsupported, + "register-bank repair does not support subregister operands"); + } + + auto Fresh = Function.registerInfo().createVirtualRegister(*OriginalInfo->get().Type); + if (!Fresh) + return std::unexpected(Fresh.error()); + auto SetBank = Function.registerInfo().setRegisterBank(*Fresh, RequiredBank); + if (!SetBank) { + auto Erased = Function.registerInfo().eraseVirtualRegister(*Fresh); + (void)Erased; + return std::unexpected(SetBank.error()); + } + + auto Copy = Operand.isDef() + ? buildBankCopy(Function, Target, Instruction, false, OriginalReg, *Fresh) + : buildBankCopy(Function, Target, Instruction, true, *Fresh, OriginalReg); + if (!Copy) { + auto Erased = Function.registerInfo().eraseVirtualRegister(*Fresh); + (void)Erased; + return std::unexpected(Copy.error()); + } + auto Replaced = Operand.setRegister(*Fresh); + if (!Replaced) { + if ((*Copy)->parent() != nullptr) { + auto Removed = (*Copy)->parent()->removeInstruction(**Copy); + (void)Removed; + } + auto Erased = Function.registerInfo().eraseVirtualRegister(*Fresh); + (void)Erased; + return std::unexpected(Replaced.error()); + } + Mutations.push_back(MappingMutation{ + .Operand = &Operand, .Original = OriginalReg, .Fresh = *Fresh, .Copy = *Copy}); + return {}; +} + +[[nodiscard]] Expected<void> rollbackMappingMutations(MachineRegisterInfo &RegInfo, + std::vector<MappingMutation> &Mutations) { + for (auto Iterator = Mutations.rbegin(); Iterator != Mutations.rend(); ++Iterator) { + auto Restored = Iterator->Operand->setRegister(Iterator->Original); + if (!Restored) + return std::unexpected(Restored.error()); + if (Iterator->Copy != nullptr && Iterator->Copy->parent() != nullptr) { + auto Removed = Iterator->Copy->parent()->removeInstruction(*Iterator->Copy); + if (!Removed) + return std::unexpected(Removed.error()); + } + auto Erased = RegInfo.eraseVirtualRegister(Iterator->Fresh); + if (!Erased) + return std::unexpected(Erased.error()); + } + Mutations.clear(); + return {}; +} + +[[nodiscard]] Expected<void> +applyInstructionMappings(MachineFunction &Function, const TargetDescription &Target, + const RegisterBankInfo &Info, + std::unordered_set<Register> &ConstrainedRegisters, + std::vector<MappingMutation> &Mutations, MachinePassResult &Result) { + MachineRegisterInfo &RegInfo = Function.registerInfo(); + const std::vector<MachineInstr *> Instructions = layoutInstructions(Function); + for (MachineInstr *Instruction : Instructions) { + auto Mapping = Info.getInstructionMapping(*Instruction, Function, Target); + if (!Mapping) + return std::unexpected(Mapping.error()); + if (Mapping->empty()) + continue; + if (Mapping->size() != Instruction->operandCount()) { + return makeUnexpected(ErrorCode::InternalError, + "target register-bank mapping has the wrong operand count"); + } + for (std::size_t Index = 0; Index < Mapping->size(); ++Index) { + if (!(*Mapping)[Index]) + continue; + const RegisterBankID RequiredBank = *Mapping->at(Index); + if (Target.registers().registerBank(RequiredBank) == nullptr) { + return makeUnexpected(ErrorCode::InternalError, + "target instruction mapping names an unknown register bank"); + } + MachineOperand *Operand = Instruction->operandAt(Index); + const Register *Reg = Operand == nullptr ? nullptr : Operand->getIf<Register>(); + if (Reg == nullptr || !Reg->isVirtual()) { + return makeUnexpected(ErrorCode::VerificationError, + "target register-bank mapping requires a virtual-register operand"); + } + auto RegisterInfo = RegInfo.virtualRegisterInfo(*Reg); + if (!RegisterInfo) + return std::unexpected(RegisterInfo.error()); + if (!RegisterInfo->get().Bank) { + auto SetBank = RegInfo.setRegisterBank(*Reg, RequiredBank); + if (!SetBank) + return std::unexpected(SetBank.error()); + ConstrainedRegisters.insert(*Reg); + Result.Changed = true; + Result.Changes.add(MachinePassChange::Registers); + continue; + } + if (*RegisterInfo->get().Bank == RequiredBank) { + ConstrainedRegisters.insert(*Reg); + continue; + } + if (!ConstrainedRegisters.contains(*Reg) && !RegisterInfo->get().RegClass) { + auto SetBank = RegInfo.setRegisterBank(*Reg, RequiredBank); + if (!SetBank) + return std::unexpected(SetBank.error()); + ConstrainedRegisters.insert(*Reg); + Result.Changed = true; + Result.Changes.add(MachinePassChange::Registers); + continue; + } + auto Repaired = + repairMappedOperand(Function, Target, *Instruction, *Operand, RequiredBank, Mutations); + if (!Repaired) + return std::unexpected(Repaired.error()); + ConstrainedRegisters.insert(Mutations.back().Fresh); + Result.Changed = true; + Result.Changes.add(MachinePassChange::Instructions); + Result.Changes.add(MachinePassChange::Operands); + Result.Changes.add(MachinePassChange::Registers); + } + } return {}; } @@ -131,7 +303,8 @@ Expected<MachinePassResult> RegBankSelect::run(MachineFunction &Function, using RegisterConfiguration = std::pair<std::optional<RegisterBankID>, std::optional<RegisterClassID>>; std::vector<std::pair<Register, RegisterConfiguration>> Snapshot; - std::vector<std::pair<Register, RegisterBankID>> PlannedBanks; + std::vector<PlannedRegisterBank> PlannedBanks; + std::unordered_set<Register> ConstrainedRegisters; for (std::size_t Index = 0; Index < RegInfo.virtualRegisters().size(); ++Index) { auto Reg = Register::makeVirtual(Index); if (!Reg) @@ -153,15 +326,18 @@ Expected<MachinePassResult> RegBankSelect::run(MachineFunction &Function, auto Bank = Info->selectBank(Function, *Reg, InfoValue, Context, Target); if (!Bank) return std::unexpected(Bank.error()); - if (Target.registers().registerBank(*Bank) == nullptr) + if (Target.registers().registerBank(Bank->Bank) == nullptr) return std::unexpected(passFailure("target register-bank selector returned an unknown bank")); - if (InfoValue.Bank && *InfoValue.Bank != *Bank) + if (InfoValue.Bank && *InfoValue.Bank != Bank->Bank) return std::unexpected( passFailure("target register-bank selector conflicts with an existing bank")); - if (InfoValue.RegClass && !Target.registers().bankCoversClass(*Bank, *InfoValue.RegClass)) + if (InfoValue.RegClass && !Target.registers().bankCoversClass(Bank->Bank, *InfoValue.RegClass)) return std::unexpected( passFailure("existing register class is not covered by selected bank")); - PlannedBanks.emplace_back(*Reg, *Bank); + PlannedBanks.push_back(PlannedRegisterBank{.Reg = *Reg, + .Bank = Bank->Bank, + .IsConstrained = Bank->IsConstrained || + InfoValue.RegClass.has_value()}); } for (MachineInstr *Instruction : layoutInstructions(Function)) { @@ -171,31 +347,37 @@ Expected<MachinePassResult> RegBankSelect::run(MachineFunction &Function, withErrorContext(Validation.error(), "register-bank mapping precondition failed: ")); } - for (const auto &[Reg, Bank] : PlannedBanks) { - const auto InfoResult = RegInfo.virtualRegisterInfo(Reg); + for (const PlannedRegisterBank &Plan : PlannedBanks) { + const auto InfoResult = RegInfo.virtualRegisterInfo(Plan.Reg); if (!InfoResult) return std::unexpected(InfoResult.error()); if (!InfoResult->get().Bank) { - auto SetBank = RegInfo.setRegisterBank(Reg, Bank); + auto SetBank = RegInfo.setRegisterBank(Plan.Reg, Plan.Bank); if (!SetBank) return std::unexpected(SetBank.error()); Result.Changed = true; Result.Changes.add(MachinePassChange::Registers); } + if (Plan.IsConstrained) + ConstrainedRegisters.insert(Plan.Reg); } + std::vector<MappingMutation> MappingMutations; const auto Restore = [&]() -> Expected<void> { + auto MappingRestore = rollbackMappingMutations(RegInfo, MappingMutations); + if (!MappingRestore) + return std::unexpected(MappingRestore.error()); return restoreRegisterConfiguration(RegInfo, Snapshot); }; + auto Mapped = applyInstructionMappings(Function, Target, *Info, ConstrainedRegisters, + MappingMutations, Result); + if (!Mapped) { + auto Restored = Restore(); + if (!Restored) + return std::unexpected(withErrorContext(Restored.error(), "register-bank rollback failed: ")); + return std::unexpected(withErrorContext(Mapped.error(), "register-bank mapping failed: ")); + } for (MachineInstr *Instruction : layoutInstructions(Function)) { - auto Constraint = Info->constrainInstruction(*Instruction, Function, Target); - if (!Constraint) { - auto Restored = Restore(); - if (!Restored) - return std::unexpected( - withErrorContext(Restored.error(), "register-bank rollback failed: ")); - return std::unexpected(Constraint.error()); - } auto Validation = Target.validateInstruction(*Instruction, Function.registerInfo()); if (!Validation) { auto Restored = Restore(); @@ -220,14 +402,6 @@ Expected<MachinePassResult> RegBankSelect::run(MachineFunction &Function, Result.Changes.add(MachinePassChange::Registers); } - auto Liveness = repairTrackedLiveness(Function, Target, Result); - if (!Liveness) { - auto Restored = Restore(); - if (!Restored) - return std::unexpected(withErrorContext(Restored.error(), "register-bank rollback failed: ")); - return std::unexpected( - withErrorContext(Liveness.error(), "register-bank liveness repair failed: ")); - } Result.Preserved = Result.Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); return Result; } diff --git a/backend/lib/CodeGen/MachineRegisterInfo.cpp b/backend/lib/CodeGen/MachineRegisterInfo.cpp index 2fe74d0..ccd7d89 100644 --- a/backend/lib/CodeGen/MachineRegisterInfo.cpp +++ b/backend/lib/CodeGen/MachineRegisterInfo.cpp @@ -255,6 +255,10 @@ RegOperandRange MachineRegisterInfo::regOperands(Register Reg) noexcept { return RegOperandRange(headFor(Reg), false, false, false); } +RegOperandRange MachineRegisterInfo::regOperands(Register Reg) const noexcept { + return RegOperandRange(headFor(Reg), false, false, false); +} + RegOperandRange MachineRegisterInfo::regNoDebugOperands(Register Reg) noexcept { return RegOperandRange(headFor(Reg), false, false, true); } diff --git a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp index 3d5ea2a..a7160b4 100644 --- a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp +++ b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp @@ -139,7 +139,7 @@ public: [[nodiscard]] Expected<void> constrainSelectedInstruction(MachineInstr &Instruction, MachineFunction &Function, const TargetDescription &Target) const override { - return RegisterBanks.constrainInstruction(Instruction, Function, Target); + return RegisterBanks.constrainSelectedInstruction(Instruction, Function, Target); } [[nodiscard]] Expected<void> canSelect(const MachineInstr &Instruction, diff --git a/backend/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp b/backend/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp index 6e38d52..7c60219 100644 --- a/backend/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp +++ b/backend/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp @@ -2,13 +2,18 @@ #include "RISCVRegisterBankInfo.h" +#include "rcc/CodeGen/GenericOpcodes.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineRegisterInfo.h" #include "rcc/CodeGen/TargetDescription.h" #include "rcc/Target/RISCV/RISCVRegisters.h" #include <cstdint> +#include <optional> #include <ranges> +#include <string_view> +#include <unordered_set> #include <utility> #include <vector> @@ -25,6 +30,181 @@ namespace { Type.scalarBitWidth() <= 64); } +[[nodiscard]] constexpr RegisterBankID gprBank() noexcept { + return RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::GPR)); +} + +[[nodiscard]] constexpr RegisterBankID fprBank() noexcept { + return RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::FPR)); +} + +[[nodiscard]] std::optional<std::size_t> operandIndex(const MachineInstr &Instruction, + const MachineOperand &Operand) noexcept { + for (std::size_t Index = 0; Index < Instruction.operandCount(); ++Index) { + if (Instruction.operandAt(Index) == &Operand) + return Index; + } + return std::nullopt; +} + +[[nodiscard]] std::optional<RegisterBankID> +physicalRegisterBank(Register Reg, const TargetDescription &Target) noexcept { + const std::optional<PhysicalRegister> Physical = Reg.physicalRegister(); + if (!Physical) + return std::nullopt; + for (const RegisterBankDesc &Bank : Target.registers().registerBanks()) { + if (Target.registers().bankContainsRegister(Bank.ID, *Physical)) + return Bank.ID; + } + return std::nullopt; +} + +[[nodiscard]] bool isFloatingGenericOpcode(GenericOpcode OpcodeValue) noexcept { + switch (OpcodeValue) { + case GenericOpcode::G_FRAME_INDEX: + case GenericOpcode::G_FREEZE: + case GenericOpcode::G_FENCE: + case GenericOpcode::G_FSHL: + case GenericOpcode::G_FSHR: + return false; + default: + return getGenericOpcodeName(OpcodeValue).starts_with("G_F"); + } +} + +[[nodiscard]] std::optional<RegisterBankID> +requiredBankForOperand(const MachineInstr &Instruction, const MachineOperand &Operand, + const TargetDescription &Target) { + const std::optional<std::size_t> Index = operandIndex(Instruction, Operand); + if (!Index) + return std::nullopt; + + if (const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction.opcode()); + Descriptor != nullptr && *Index < Descriptor->Operands.size() && + Descriptor->Operands[*Index].RegBank) { + return Descriptor->Operands[*Index].RegBank; + } + + if (Instruction.opcode() == opcode(GenericOpcode::COPY)) { + if (!Operand.isDef()) + return std::nullopt; + for (const MachineOperand &Other : Instruction.operands()) { + if (&Other == &Operand) + continue; + const Register *OtherReg = Other.getIf<Register>(); + if (OtherReg != nullptr && Other.isUse() && OtherReg->isPhysical()) + return physicalRegisterBank(*OtherReg, Target); + } + return std::nullopt; + } + + if (!isGenericOpcode(Instruction.opcode())) + return std::nullopt; + const GenericOpcode Generic = static_cast<GenericOpcode>(Instruction.opcode().value()); + switch (Generic) { + case GenericOpcode::G_FPEXTLOAD: + case GenericOpcode::G_FPTRUNCSTORE: + return *Index == 0 ? fprBank() : gprBank(); + case GenericOpcode::G_FPTOSI: + case GenericOpcode::G_FPTOUI: + case GenericOpcode::G_FPTOSI_SAT: + case GenericOpcode::G_FPTOUI_SAT: + case GenericOpcode::G_LROUND: + case GenericOpcode::G_LLROUND: + return *Index == 0 ? gprBank() : fprBank(); + case GenericOpcode::G_SITOFP: + case GenericOpcode::G_UITOFP: + return *Index == 0 ? fprBank() : gprBank(); + case GenericOpcode::G_FCMP: + case GenericOpcode::G_IS_FPCLASS: + return *Index == 0 ? gprBank() : fprBank(); + case GenericOpcode::G_FPOWI: + case GenericOpcode::G_FLDEXP: + return *Index < 2 ? fprBank() : gprBank(); + case GenericOpcode::G_FFREXP: + return *Index == 1 ? gprBank() : fprBank(); + case GenericOpcode::G_LOAD: + case GenericOpcode::G_SEXTLOAD: + case GenericOpcode::G_ZEXTLOAD: + return *Index == 1 ? std::optional<RegisterBankID>(gprBank()) : std::nullopt; + case GenericOpcode::G_STORE: + return *Index == 1 ? std::optional<RegisterBankID>(gprBank()) : std::nullopt; + case GenericOpcode::G_SELECT: + return *Index == 1 ? std::optional<RegisterBankID>(gprBank()) : std::nullopt; + case GenericOpcode::G_IMPLICIT_DEF: + case GenericOpcode::G_PHI: + return std::nullopt; + default: + return isFloatingGenericOpcode(Generic) ? fprBank() : gprBank(); + } +} + +void appendConnectedRegisters(const MachineInstr &Instruction, const MachineOperand &Operand, + std::vector<Register> &Pending) { + const std::optional<std::size_t> Index = operandIndex(Instruction, Operand); + if (!Index) + return; + const bool IsPhi = Instruction.opcode() == opcode(GenericOpcode::G_PHI); + if (!IsPhi) + return; + for (std::size_t OtherIndex = 0; OtherIndex < Instruction.operandCount(); ++OtherIndex) { + if (OtherIndex == *Index) + continue; + const MachineOperand *Other = Instruction.operandAt(OtherIndex); + const Register *OtherReg = Other == nullptr ? nullptr : Other->getIf<Register>(); + if (OtherReg != nullptr && OtherReg->isVirtual()) + Pending.push_back(*OtherReg); + } +} + +[[nodiscard]] Expected<std::optional<RegisterBankID>> +requiredBankForRegister(const MachineFunction &Function, Register Reg, + const TargetDescription &Target) { + std::vector<Register> Pending{Reg}; + std::unordered_set<Register> Visited; + std::optional<RegisterBankID> Required; + while (!Pending.empty()) { + const Register Current = Pending.back(); + Pending.pop_back(); + if (!Visited.insert(Current).second) + continue; + for (const MachineOperand &Operand : Function.registerInfo().regOperands(Current)) { + const MachineInstr *Instruction = Operand.parent(); + if (Instruction == nullptr) + continue; + const std::optional<RegisterBankID> OperandBank = + requiredBankForOperand(*Instruction, Operand, Target); + if (OperandBank) { + if (Required && *Required != *OperandBank) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V virtual register has conflicting register-bank uses"); + } + Required = *OperandBank; + } + appendConnectedRegisters(*Instruction, Operand, Pending); + } + } + return Required; +} + +[[nodiscard]] bool hasFloatingCopyUse(const MachineFunction &Function, Register Reg, + const TargetDescription &Target) { + for (const MachineOperand &Operand : Function.registerInfo().regOperands(Reg)) { + const MachineInstr *Instruction = Operand.parent(); + if (!Operand.isUse() || Instruction == nullptr || + Instruction->opcode() != opcode(GenericOpcode::COPY)) + continue; + for (const MachineOperand &Other : Instruction->operands()) { + const Register *OtherReg = Other.getIf<Register>(); + if (!Other.isDef() || OtherReg == nullptr || !OtherReg->isPhysical()) + continue; + if (physicalRegisterBank(*OtherReg, Target) == fprBank()) + return true; + } + } + return false; +} + [[nodiscard]] Expected<RegisterClassID> requiredRegisterClass(const MachineFunction &Function, Register Reg, const TargetDescription &Target) { @@ -41,12 +221,18 @@ namespace { const VirtualRegisterInfo &RegisterInfo = Info->get(); const LLT Type = RegisterInfo.Type.value_or(LLT{}); RegisterClassID RequiredClass; - if (isScalarGPRType(Type)) { + if ((RegisterInfo.Bank && *RegisterInfo.Bank == fprBank()) || Type.isFloat()) { + if (Type.scalarBitWidth() != 32 && Type.scalarBitWidth() != 64) { + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V RV64GC has no floating register class for type '" + + Type.toString() + "'"); + } + RequiredClass = + Type.scalarBitWidth() == 32 + ? RegisterClassID::fromValue(static_cast<std::uint32_t>(RegisterClass::FPR32)) + : RegisterClassID::fromValue(static_cast<std::uint32_t>(RegisterClass::FPR64)); + } else if (isScalarGPRType(Type)) { RequiredClass = RegisterClassID::fromValue(static_cast<std::uint32_t>(RegisterClass::GPR)); - } else if (Type.isFloat() && Type.scalarBitWidth() == 32) { - RequiredClass = RegisterClassID::fromValue(static_cast<std::uint32_t>(RegisterClass::FPR32)); - } else if (Type.isFloat() && Type.scalarBitWidth() == 64) { - RequiredClass = RegisterClassID::fromValue(static_cast<std::uint32_t>(RegisterClass::FPR64)); } else { return makeUnexpected(ErrorCode::Unsupported, "RISC-V RV64GC has no register class for type '" + Type.toString() + "'"); @@ -70,26 +256,76 @@ namespace { } // namespace -Expected<RegisterBankID> RISCVRegisterBankInfo::selectBank(const MachineFunction &, Register, - const VirtualRegisterInfo &Info, - const MachineInstr *, - const TargetDescription &) const { +Expected<RegisterBankSelection> +RISCVRegisterBankInfo::selectBank(const MachineFunction &Function, Register Reg, + const VirtualRegisterInfo &Info, const MachineInstr *, + const TargetDescription &Target) const { if (!Info.Type) { return makeUnexpected(ErrorCode::VerificationError, "RISC-V register-bank selection requires a virtual-register type"); } - if (isScalarGPRType(*Info.Type)) - return RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::GPR)); - if (isRVFloatType(*Info.Type)) - return RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::FPR)); - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V RV64GC has no register bank for virtual register type '" + - Info.Type->toString() + "'"); + + auto Required = requiredBankForRegister(Function, Reg, Target); + if (!Required) + return std::unexpected(Required.error()); + const bool IsConstrained = Required->has_value() || Info.Bank.has_value(); + const RegisterBankID Selected = + Required->value_or(isRVFloatType(*Info.Type) ? fprBank() : gprBank()); + if (Info.Bank && *Info.Bank != Selected) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V virtual register has a conflicting register bank"); + } + return RegisterBankSelection{.Bank = Selected, .IsConstrained = IsConstrained}; +} + +Expected<RegisterBankInstructionMapping> +RISCVRegisterBankInfo::getInstructionMapping(const MachineInstr &Instruction, + const MachineFunction &Function, + const TargetDescription &Target) const { + if (Instruction.opcode() != opcode(GenericOpcode::G_SELECT)) + return RegisterBankInstructionMapping{}; + if (Instruction.operandCount() != 4) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V G_SELECT register-bank mapping requires four operands"); + } + + RegisterBankInstructionMapping Mapping(Instruction.operandCount()); + Mapping[1] = gprBank(); + unsigned FloatingConstraints = 0; + + const Register *Result = Instruction.operandAt(0)->getIf<Register>(); + if (Result == nullptr || !Result->isVirtual()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V G_SELECT result must be a virtual register"); + } + auto ResultRequirement = requiredBankForRegister(Function, *Result, Target); + if (!ResultRequirement) + return std::unexpected(ResultRequirement.error()); + if (*ResultRequirement == fprBank() || hasFloatingCopyUse(Function, *Result, Target)) + ++FloatingConstraints; + + for (const std::size_t Index : {std::size_t{2}, std::size_t{3}}) { + const Register *Source = Instruction.operandAt(Index)->getIf<Register>(); + if (Source == nullptr || !Source->isVirtual()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V G_SELECT sources must be virtual registers"); + } + auto SourceInfo = Function.registerInfo().virtualRegisterInfo(*Source); + if (!SourceInfo) + return std::unexpected(SourceInfo.error()); + if (SourceInfo->get().Bank == fprBank()) + ++FloatingConstraints; + } + + const RegisterBankID ValueBank = FloatingConstraints >= 2 ? fprBank() : gprBank(); + Mapping[0] = ValueBank; + Mapping[2] = ValueBank; + Mapping[3] = ValueBank; + return Mapping; } -Expected<void> RISCVRegisterBankInfo::constrainInstruction(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) const { +Expected<void> RISCVRegisterBankInfo::constrainSelectedInstruction( + MachineInstr &Instruction, MachineFunction &Function, const TargetDescription &Target) const { std::vector<std::pair<Register, RegisterClassID>> PlannedClasses; for (const MachineOperand &Operand : Instruction.operands()) { const Register *Reg = Operand.getIf<Register>(); @@ -128,7 +364,7 @@ Expected<void> RISCVRegisterBankInfo::constrainRegister(MachineFunction &Functio if (!RequiredClass) return std::unexpected(RequiredClass.error()); if (!Info->get().Bank) { - auto SetBank = Function.registerInfo().setRegisterBank(Reg, *Bank); + auto SetBank = Function.registerInfo().setRegisterBank(Reg, Bank->Bank); if (!SetBank) return std::unexpected(SetBank.error()); } diff --git a/backend/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.h b/backend/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.h index 543bc4d..69eb539 100644 --- a/backend/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.h +++ b/backend/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.h @@ -9,13 +9,15 @@ namespace rcc::riscv { class RISCVRegisterBankInfo final : public RegisterBankInfo { public: - [[nodiscard]] Expected<RegisterBankID> selectBank(const MachineFunction &Function, Register Reg, - const VirtualRegisterInfo &Info, - const MachineInstr *Instruction, - const TargetDescription &Target) const override; - [[nodiscard]] Expected<void> constrainInstruction(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) const override; + [[nodiscard]] Expected<RegisterBankSelection> + selectBank(const MachineFunction &Function, Register Reg, const VirtualRegisterInfo &Info, + const MachineInstr *Instruction, const TargetDescription &Target) const override; + [[nodiscard]] Expected<RegisterBankInstructionMapping> + getInstructionMapping(const MachineInstr &Instruction, const MachineFunction &Function, + const TargetDescription &Target) const override; + [[nodiscard]] Expected<void> constrainSelectedInstruction(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) const; [[nodiscard]] Expected<void> constrainRegister(MachineFunction &Function, Register Reg, const TargetDescription &Target) const; [[nodiscard]] Expected<void> validateInstruction(const MachineInstr &Instruction, |
