aboutsummaryrefslogtreecommitdiff
path: root/backend/lib
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib')
-rw-r--r--backend/lib/CodeGen/GlobalISel/RegBankSelect.cpp246
-rw-r--r--backend/lib/CodeGen/MachineRegisterInfo.cpp4
-rw-r--r--backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp2
-rw-r--r--backend/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp276
-rw-r--r--backend/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.h16
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,