diff options
Diffstat (limited to 'backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp')
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp b/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp new file mode 100644 index 0000000..3b3a205 --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "RISCV.h" +#include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineFunctionRecords.h" +#include "rcc/CodeGen/MachineIRBuilder.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/MachineRegisterInfo.h" +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/Target/RISCV/RISCVInstructions.h" +#include "rcc/Target/RISCV/RISCVRegisters.h" +#include "rcc/Target/RISCV/RISCVTarget.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <string> +#include <string_view> +#include <utility> + +namespace rcc::riscv { +namespace { + +constexpr std::uint32_t PCRelativeHighFlag = 2; +constexpr std::uint32_t PCRelativeLowFlag = 4; +constexpr std::uint32_t CopyF32Flag = 8; +constexpr std::uint32_t CopyF64Flag = 16; +constexpr std::uint32_t GOTPCRelativeHighFlag = 128; + +[[nodiscard]] std::optional<RegisterBankID> +registerBank(Register Reg, const MachineFunction &Function, const TargetDescription &Target) { + if (Reg.isVirtual()) { + auto Info = Function.registerInfo().virtualRegisterInfo(Reg); + return Info ? Info->get().Bank : std::nullopt; + } + 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]] std::optional<LowLevelType> registerType(Register Reg, + const MachineFunction &Function) { + if (!Reg.isVirtual()) + return std::nullopt; + auto Info = Function.registerInfo().virtualRegisterInfo(Reg); + return Info ? Info->get().Type : std::nullopt; +} + +[[nodiscard]] Expected<void> copyLocation(const MachineInstr &Source, MachineInstr &Destination) { + if (Source.sourceLocation()) + Destination.setSourceLocation(*Source.sourceLocation()); + if (Source.debugLocation()) { + auto Debug = Destination.setDebugLocation(*Source.debugLocation()); + if (!Debug) + return std::unexpected(Debug.error()); + } + if (Source.origin()) + Destination.setOrigin(*Source.origin()); + return {}; +} + +[[nodiscard]] Expected<void> copyCommonDecoration(const MachineInstr &Source, + MachineInstr &Destination, bool CopyPreSymbol, + bool CopyPostSymbol, bool CopyDebugNumber) { + auto Location = copyLocation(Source, Destination); + if (!Location) + return std::unexpected(Location.error()); + Destination.setFlags(Source.flags()); + if (CopyDebugNumber && Source.debugInstrNumber() != 0) { + auto Number = Destination.setDebugInstrNumber(Source.debugInstrNumber()); + if (!Number) + return std::unexpected(Number.error()); + } + if (CopyPreSymbol) { + if (const auto Symbol = Source.preInstrSymbol()) { + auto Set = Destination.setPreInstrSymbol(std::string(*Symbol)); + if (!Set) + return std::unexpected(Set.error()); + } + } + if (CopyPostSymbol) { + if (const auto Symbol = Source.postInstrSymbol()) { + auto Set = Destination.setPostInstrSymbol(std::string(*Symbol)); + if (!Set) + return std::unexpected(Set.error()); + } + } + if (Source.heapAllocMarker()) { + auto Set = Destination.setHeapAllocMarker(*Source.heapAllocMarker()); + if (!Set) + return std::unexpected(Set.error()); + } + if (Source.pcSections()) { + auto Set = Destination.setPCSections(*Source.pcSections()); + if (!Set) + return std::unexpected(Set.error()); + } + if (Source.mmra()) { + auto Set = Destination.setMMRA(*Source.mmra()); + if (!Set) + return std::unexpected(Set.error()); + } + if (Source.cfiType()) { + auto Set = Destination.setCFIType(*Source.cfiType()); + if (!Set) + return std::unexpected(Set.error()); + } + if (const auto Symbol = Source.deactivationSymbol()) { + auto Set = Destination.setDeactivationSymbol(std::string(*Symbol)); + if (!Set) + return std::unexpected(Set.error()); + } + return {}; +} + +[[nodiscard]] Expected<std::pair<Register, Register>> +copyRegisters(const MachineInstr &Instruction) { + const MachineOperand *DestinationOperand = Instruction.operandAt(0); + const MachineOperand *SourceOperand = Instruction.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) + return makeUnexpected(ErrorCode::VerificationError, + "post-RA copy pseudo requires two register operands"); + return std::pair{*Destination, *Source}; +} + +[[nodiscard]] Expected<bool> prepareCopy(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target) { + if (Instruction.opcode() != opcode(GenericOpcode::COPY)) + return false; + auto Registers = copyRegisters(Instruction); + if (!Registers) + return std::unexpected(Registers.error()); + const auto DestinationBank = registerBank(Registers->first, Function, Target); + const auto SourceBank = registerBank(Registers->second, Function, Target); + if (!DestinationBank || !SourceBank) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V copy preparation requires constrained register banks"); + if (*DestinationBank == *SourceBank) + return false; + + const auto DestinationType = registerType(Registers->first, Function); + const auto SourceType = registerType(Registers->second, Function); + const std::optional<LowLevelType> Type = DestinationType ? DestinationType : SourceType; + if (!Type || Type->isVector() || (Type->scalarBitWidth() != 32 && Type->scalarBitWidth() != 64)) { + return makeUnexpected(ErrorCode::Unsupported, + "RISC-V cross-bank COPY requires a scalar 32-bit or 64-bit type"); + } + if (DestinationType && SourceType && + DestinationType->scalarBitWidth() != SourceType->scalarBitWidth()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V cross-bank COPY has mismatched value widths"); + } + const std::uint32_t Flag = Type->scalarBitWidth() == 32 ? CopyF32Flag : CopyF64Flag; + MachineOperand *DestinationOperand = Instruction.operandAt(0); + MachineOperand *SourceOperand = Instruction.operandAt(1); + const bool Changed = + DestinationOperand->targetFlags() != Flag || SourceOperand->targetFlags() != Flag; + DestinationOperand->setTargetFlags(Flag); + SourceOperand->setTargetFlags(Flag); + return Changed; +} + +[[nodiscard]] Expected<bool> expandCopy(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target) { + auto Registers = copyRegisters(Instruction); + if (!Registers) + return std::unexpected(Registers.error()); + const Register Destination = Registers->first; + const Register Source = Registers->second; + if (!Destination.isPhysical() || !Source.isPhysical()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V post-RA COPY retains a virtual register"); + } + if (Destination == Source) { + auto Removed = Instruction.eraseFromParent(); + if (!Removed) + return std::unexpected(Removed.error()); + return true; + } + const auto DestinationBank = registerBank(Destination, Function, Target); + const auto SourceBank = registerBank(Source, Function, Target); + if (!DestinationBank || !SourceBank) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V post-RA COPY references an unknown register bank"); + const RegisterBankID GPR = + RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::GPR)); + const RegisterBankID FPR = + RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::FPR)); + + InstructionOpcode Selected = InstructionOpcode::Invalid; + bool DuplicateSource = false; + bool AddZeroImmediate = false; + if (*DestinationBank == GPR && *SourceBank == GPR) { + Selected = InstructionOpcode::ADDI; + AddZeroImmediate = true; + } else if (*DestinationBank == FPR && *SourceBank == FPR) { + Selected = InstructionOpcode::FSGNJ_D; + DuplicateSource = true; + } else { + const std::uint32_t DestinationFlags = Instruction.operandAt(0)->targetFlags(); + const std::uint32_t SourceFlags = Instruction.operandAt(1)->targetFlags(); + if (DestinationFlags != SourceFlags || + (DestinationFlags != CopyF32Flag && DestinationFlags != CopyF64Flag)) { + return makeUnexpected(ErrorCode::PseudoExpansionFailed, + "RISC-V cross-bank COPY has no prepared transfer width"); + } + if (*DestinationBank == GPR && *SourceBank == FPR) { + Selected = + DestinationFlags == CopyF32Flag ? InstructionOpcode::FMV_X_W : InstructionOpcode::FMV_X_D; + } else if (*DestinationBank == FPR && *SourceBank == GPR) { + Selected = + DestinationFlags == CopyF32Flag ? InstructionOpcode::FMV_W_X : InstructionOpcode::FMV_D_X; + } + } + if (Selected == InstructionOpcode::Invalid) + return makeUnexpected(ErrorCode::Unsupported, "RISC-V COPY crosses unsupported banks"); + + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + auto Pending = Builder.buildInstr(instructionOpcode(Selected)); + if (!Pending) + return std::unexpected(Pending.error()); + auto Def = Pending->addDef(Destination); + if (!Def) + return std::unexpected(Def.error()); + RegisterOperandFlags SourceFlags; + const RegisterOperandFlags OriginalSourceFlags = Instruction.operandAt(1)->registerFlags(); + if (OriginalSourceFlags.has(RegisterOperandFlag::Kill)) + SourceFlags.set(RegisterOperandFlag::Kill); + if (*DestinationBank == GPR && *SourceBank == GPR && + OriginalSourceFlags.has(RegisterOperandFlag::Renamable)) { + SourceFlags.set(RegisterOperandFlag::Renamable); + } + auto Use = Pending->addUse(Source, SourceFlags); + if (!Use) + return std::unexpected(Use.error()); + if (DuplicateSource) { + auto SecondUse = Pending->addUse(Source); + if (!SecondUse) + return std::unexpected(SecondUse.error()); + } + if (AddZeroImmediate) { + auto Immediate = Pending->addImmediate(0); + if (!Immediate) + return std::unexpected(Immediate.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 = Instruction.eraseFromParent(); + if (!Removed) + return std::unexpected(Removed.error()); + return true; +} + +[[nodiscard]] Expected<bool> expandSubregisterCopy(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) { + const Opcode Value = Instruction.opcode(); + if (Value == opcode(GenericOpcode::EXTRACT_SUBREG) || + Value == opcode(GenericOpcode::SUBREG_TO_REG)) { + const MachineOperand *Index = Instruction.operandAt(2); + const auto *Immediate = Index == nullptr ? nullptr : Index->getIf<std::int64_t>(); + if (Immediate == nullptr || *Immediate != 0) + return makeUnexpected(ErrorCode::Unsupported, + "RV64GC does not support nonzero subregister indices"); + } else if (Value == opcode(GenericOpcode::COPY_LANEMASK)) { + const MachineOperand *MaskOperand = Instruction.operandAt(2); + const auto *Mask = MaskOperand == nullptr ? nullptr : MaskOperand->getIf<LaneMaskOperand>(); + if (Mask == nullptr || Mask->Mask.empty()) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V lane copy requires a nonempty lane mask"); + } else { + return false; + } + return expandCopy(Instruction, Function, Target); +} + +[[nodiscard]] Expected<void> addDirectTarget(MachineInstrBuilder &Builder, + const MachineOperand &Callee) { + if (const auto *Global = Callee.getIf<GlobalSymbolOperand>()) + return Builder.addGlobalSymbol(Global->Name, Global->Offset, PCRelativeHighFlag); + if (const auto *External = Callee.getIf<ExternalSymbolOperand>()) + return Builder.addExternalSymbol(External->Name, External->Offset, PCRelativeHighFlag); + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V direct call pseudo has no symbol target"); +} + +[[nodiscard]] Expected<Register> createGPRScratchRegister(MachineFunction &Function, + const TargetDescription &Target) { + const RegisterBankID GPRBank = + RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::GPR)); + const RegisterClassID GPRClass = + RegisterClassID::fromValue(static_cast<std::uint32_t>(RegisterClass::GPR)); + if (Target.registers().registerBank(GPRBank) == nullptr || + Target.registers().registerClass(GPRClass) == nullptr) { + return makeUnexpected(ErrorCode::InternalError, + "RISC-V pre-RA address expansion requires the GPR register class"); + } + auto Scratch = Function.registerInfo().createVirtualRegister(); + if (!Scratch) + return std::unexpected(Scratch.error()); + auto SetBank = Function.registerInfo().setRegisterBank(*Scratch, GPRBank); + if (!SetBank) + return std::unexpected(SetBank.error()); + auto SetClass = Function.registerInfo().setRegisterClass(*Scratch, GPRClass); + if (!SetClass) + return std::unexpected(SetClass.error()); + return *Scratch; +} + +[[nodiscard]] Expected<bool> expandAddressPseudo(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target, + bool LoadFromGOT) { + const MachineOperand *Destination = Instruction.operandAt(0); + const MachineOperand *Symbol = Instruction.operandAt(1); + const Register *DestinationRegister = + Destination == nullptr ? nullptr : Destination->getIf<Register>(); + if (DestinationRegister == nullptr || Symbol == nullptr || Instruction.operandCount() != 2) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V address pseudo has malformed explicit operands"); + } + if (LoadFromGOT && Instruction.memOperands().size() != 1) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V global-address pseudo requires one GOT load memory operand"); + } + if (!LoadFromGOT && !Instruction.memOperands().empty()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V local-address pseudo cannot carry memory operands"); + } + + auto Scratch = createGPRScratchRegister(Function, Target); + if (!Scratch) + return std::unexpected(Scratch.error()); + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + + auto HighPending = Builder.buildInstr(instructionOpcode(InstructionOpcode::AUIPC)); + if (!HighPending) + return std::unexpected(HighPending.error()); + auto HighDef = HighPending->addDef(*Scratch); + if (!HighDef) + return std::unexpected(HighDef.error()); + MachineOperand HighSymbol = Symbol->cloneDetached(); + HighSymbol.setTargetFlags(LoadFromGOT ? GOTPCRelativeHighFlag : PCRelativeHighFlag); + auto HighTarget = HighPending->addOperand(std::move(HighSymbol)); + if (!HighTarget) + return std::unexpected(HighTarget.error()); + auto High = HighPending->build(); + if (!High) + return std::unexpected(High.error()); + const std::string Anchor = Instruction.preInstrSymbol() + ? std::string(*Instruction.preInstrSymbol()) + : ".Lrcc_pcrel_hi_" + std::to_string(Function.stableID().value()) + + "_" + std::to_string((*High)->stableID().value()); + auto HighAnchor = (*High)->setPreInstrSymbol(Anchor); + if (!HighAnchor) + return std::unexpected(HighAnchor.error()); + auto HighDecoration = copyCommonDecoration(Instruction, **High, false, false, false); + if (!HighDecoration) + return std::unexpected(HighDecoration.error()); + + const InstructionOpcode LowOpcode = LoadFromGOT ? InstructionOpcode::LD : InstructionOpcode::ADDI; + auto LowPending = Builder.buildInstr(instructionOpcode(LowOpcode)); + if (!LowPending) + return std::unexpected(LowPending.error()); + auto LowDef = LowPending->addOperand(Destination->cloneDetached()); + if (!LowDef) + return std::unexpected(LowDef.error()); + auto LowBase = LowPending->addUse(*Scratch); + if (!LowBase) + return std::unexpected(LowBase.error()); + auto LowOffset = LowPending->addMCSymbol(Anchor, 0, PCRelativeLowFlag); + if (!LowOffset) + return std::unexpected(LowOffset.error()); + if (LoadFromGOT) { + const MachineMemOperand *Memory = Instruction.memOperands().front(); + if (Memory == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V global-address pseudo has a null memory operand"); + auto AddedMemory = LowPending->addMemOperand(*Memory); + if (!AddedMemory) + return std::unexpected(AddedMemory.error()); + } + auto Low = LowPending->build(); + if (!Low) + return std::unexpected(Low.error()); + auto LowDecoration = copyCommonDecoration(Instruction, **Low, false, true, true); + if (!LowDecoration) + return std::unexpected(LowDecoration.error()); + auto Records = + Function.records().replaceInstructionReference(Instruction.stableID(), (*Low)->stableID()); + if (!Records) + return std::unexpected(Records.error()); + auto Removed = Instruction.eraseFromParent(); + if (!Removed) + return std::unexpected(Removed.error()); + return true; +} + +[[nodiscard]] bool isRegister(const MachineOperand &Operand, PhysicalRegister Reg) noexcept { + const Register *Value = Operand.getIf<Register>(); + return Value != nullptr && Value->physicalRegister() == Reg; +} + +[[nodiscard]] Expected<void> copyExtraImplicitOperands( + const MachineInstr &Source, std::size_t FirstImplicit, MachineInstrBuilder &Destination, + std::optional<PhysicalRegister> ExplicitDef, std::optional<PhysicalRegister> ExplicitUse) { + for (std::size_t Index = FirstImplicit; Index < Source.operandCount(); ++Index) { + const MachineOperand *Operand = Source.operandAt(Index); + if (Operand == nullptr || !Operand->isImplicit()) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V call pseudo has malformed implicit operands"); + if ((ExplicitDef && Operand->isDef() && isRegister(*Operand, *ExplicitDef)) || + (ExplicitUse && Operand->isUse() && isRegister(*Operand, *ExplicitUse))) { + continue; + } + auto Added = Destination.addImplicitOperand(Operand->cloneDetached()); + if (!Added) + return std::unexpected(Added.error()); + } + return {}; +} + +[[nodiscard]] Expected<bool> expandCall(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target, bool IsTail) { + MachineBasicBlock *Block = Instruction.parent(); + const bool HasExplicitLink = + Instruction.opcode() == instructionOpcode(InstructionOpcode::PseudoCALLReg); + const std::size_t CalleeIndex = HasExplicitLink ? 1U : 0U; + const MachineOperand *Callee = Instruction.operandAt(CalleeIndex); + const MachineOperand *Mask = Instruction.operandAt(CalleeIndex + 1U); + const bool HasMask = + Mask != nullptr && !Mask->isImplicit() && Mask->kind() == MachineOperand::Kind::RegisterMask; + if (Block == nullptr || Callee == nullptr || + (Mask != nullptr && !Mask->isImplicit() && !HasMask)) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V call pseudo has malformed explicit operands"); + } + auto Zero = physicalRegister(PhysicalRegisterID::X0); + auto Link = physicalRegister(PhysicalRegisterID::X1); + auto Scratch = physicalRegister(PhysicalRegisterID::X6); + if (!Zero || !Link || !Scratch) + return makeUnexpected(ErrorCode::InternalError, "RISC-V call registers are unavailable"); + PhysicalRegister DestinationRegister = IsTail ? *Zero : *Link; + if (HasExplicitLink) { + const MachineOperand *Destination = Instruction.operandAt(0); + const Register *DestinationValue = + Destination == nullptr ? nullptr : Destination->getIf<Register>(); + const auto PhysicalDestination = + DestinationValue == nullptr ? std::nullopt : DestinationValue->physicalRegister(); + if (!PhysicalDestination || !Destination->isDef()) { + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V call-register pseudo has an invalid link register"); + } + DestinationRegister = *PhysicalDestination; + } + + MachineIRBuilder Builder(Function, Target); + auto Point = Builder.setInsertPointBefore(Instruction); + if (!Point) + return std::unexpected(Point.error()); + Register Base; + std::optional<std::string> Anchor; + const bool Direct = Callee->kind() == MachineOperand::Kind::GlobalSymbol || + Callee->kind() == MachineOperand::Kind::ExternalSymbol; + if (Direct) { + const PhysicalRegister HighRegister = IsTail ? *Scratch : DestinationRegister; + Base = Register::fromPhysical(HighRegister); + Anchor = Instruction.preInstrSymbol() + ? std::optional<std::string>{std::string(*Instruction.preInstrSymbol())} + : std::optional<std::string>{".Lrcc_call_" + + std::to_string(Function.stableID().value()) + "_" + + std::to_string(Instruction.stableID().value())}; + auto HighPending = Builder.buildInstr(instructionOpcode(InstructionOpcode::AUIPC)); + if (!HighPending) + return std::unexpected(HighPending.error()); + auto HighDef = HighPending->addDef(Base); + if (!HighDef) + return std::unexpected(HighDef.error()); + auto HighTarget = addDirectTarget(*HighPending, *Callee); + if (!HighTarget) + return std::unexpected(HighTarget.error()); + auto High = HighPending->build(); + if (!High) + return std::unexpected(High.error()); + auto Symbol = (*High)->setPreInstrSymbol(*Anchor); + if (!Symbol) + return std::unexpected(Symbol.error()); + auto Decoration = copyLocation(Instruction, **High); + if (!Decoration) + return std::unexpected(Decoration.error()); + } else { + const Register *CalleeRegister = Callee->getIf<Register>(); + if (CalleeRegister == nullptr || !CalleeRegister->isPhysical()) + return makeUnexpected(ErrorCode::VerificationError, + "RISC-V indirect call retains a virtual callee register"); + Base = *CalleeRegister; + } + + const InstructionOpcode LowOpcode = + IsTail ? InstructionOpcode::JALR_TAIL : InstructionOpcode::JALR_CALL; + auto LowPending = Builder.buildInstr(instructionOpcode(LowOpcode)); + if (!LowPending) + return std::unexpected(LowPending.error()); + auto LowDef = LowPending->addDef(Register::fromPhysical(DestinationRegister)); + if (!LowDef) + return std::unexpected(LowDef.error()); + auto LowBase = LowPending->addUse(Base); + if (!LowBase) + return std::unexpected(LowBase.error()); + Expected<void> LowOffset = + Anchor ? LowPending->addMCSymbol(*Anchor, 0, PCRelativeLowFlag) : LowPending->addImmediate(0); + if (!LowOffset) + return std::unexpected(LowOffset.error()); + if (HasMask) { + auto LowMask = LowPending->addOperand(Mask->cloneDetached()); + if (!LowMask) + return std::unexpected(LowMask.error()); + } + const std::size_t FirstImplicit = CalleeIndex + 1U + static_cast<std::size_t>(HasMask); + auto Implicit = copyExtraImplicitOperands( + Instruction, FirstImplicit, *LowPending, + IsTail ? std::nullopt : std::optional<PhysicalRegister>{DestinationRegister}, std::nullopt); + if (!Implicit) + return std::unexpected(Implicit.error()); + auto Low = LowPending->build(); + if (!Low) + return std::unexpected(Low.error()); + auto Decoration = copyCommonDecoration(Instruction, **Low, !Direct, true, true); + if (!Decoration) + return std::unexpected(Decoration.error()); + auto Records = + Function.records().replaceInstructionReference(Instruction.stableID(), (*Low)->stableID()); + if (!Records) + return std::unexpected(Records.error()); + auto Removed = Instruction.eraseFromParent(); + if (!Removed) + return std::unexpected(Removed.error()); + return true; +} + +class RISCVPseudoExpansionInfo final : public TargetPseudoExpansionInfo { +public: + [[nodiscard]] Expected<bool> preparePostRAPseudo(MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) const override { + return prepareCopy(Instruction, Function, Target); + } + + [[nodiscard]] Expected<bool> expandPseudo(MachineInstr &Instruction, + MachinePseudoExpansionPhase Phase, + MachineFunction &Function, + const TargetDescription &Target) const override { + if (Phase == MachinePseudoExpansionPhase::PreRA) { + if (Instruction.opcode() == instructionOpcode(InstructionOpcode::PseudoLLA)) + return expandAddressPseudo(Instruction, Function, Target, false); + if (Instruction.opcode() == instructionOpcode(InstructionOpcode::PseudoLGA)) + return expandAddressPseudo(Instruction, Function, Target, true); + return false; + } + if (Phase == MachinePseudoExpansionPhase::PostRA) { + if (Instruction.opcode() == opcode(GenericOpcode::COPY)) + return expandCopy(Instruction, Function, Target); + return expandSubregisterCopy(Instruction, Function, Target); + } + if (Phase == MachinePseudoExpansionPhase::TargetLate) { + if (Instruction.opcode() == instructionOpcode(InstructionOpcode::PseudoCALL)) + return expandCall(Instruction, Function, Target, false); + if (Instruction.opcode() == instructionOpcode(InstructionOpcode::PseudoCALLReg)) + return expandCall(Instruction, Function, Target, false); + if (Instruction.opcode() == instructionOpcode(InstructionOpcode::PseudoTAIL)) + return expandCall(Instruction, Function, Target, true); + } + return false; + } +}; + +} // namespace + +const TargetPseudoExpansionInfo &pseudoExpansionInfo() noexcept { + static const RISCVPseudoExpansionInfo Value; + return Value; +} + +} // namespace rcc::riscv |
