aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-09 17:03:19 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-09 17:03:19 +0800
commit495c675771f43cfa12ab7a426a538be72fec1805 (patch)
tree4107223d6c6ef2e74d56064aa21b196307502342 /backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp
parentda8bc76b4bf7d1a7b025d6c8ef5809551467dcc7 (diff)
downloadrcc-project-495c675771f43cfa12ab7a426a538be72fec1805.tar.gz
rcc-project-495c675771f43cfa12ab7a426a538be72fec1805.tar.xz
[CodeGen] Implement machine optimization and scheduling
Diffstat (limited to 'backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp')
-rw-r--r--backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp512
1 files changed, 512 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..ed73794
--- /dev/null
+++ b/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#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/RISCVCodeGen.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;
+
+[[nodiscard]] constexpr Opcode targetOpcode(InstructionOpcode Value) noexcept {
+ return Opcode::fromValue(static_cast<std::uint32_t>(Value));
+}
+
+[[nodiscard]] Expected<PhysicalRegister> physical(PhysicalRegisterID ID) {
+ auto Reg = physicalRegister(ID);
+ if (!Reg)
+ return std::unexpected(Reg.error());
+ return *Reg;
+}
+
+[[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<LLT> 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<void> removeOldInstruction(MachineInstr &Instruction) {
+ MachineBasicBlock *Block = Instruction.parent();
+ if (Block == nullptr)
+ return makeUnexpected(ErrorCode::InvalidArgument, "pseudo instruction is not linked");
+ return Block->removeInstruction(Instruction);
+}
+
+[[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<LLT> 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 = removeOldInstruction(Instruction);
+ 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(targetOpcode(Selected));
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ auto Def = Pending->addDef(Destination);
+ if (!Def)
+ return std::unexpected(Def.error());
+ auto Use = Pending->addUse(Source);
+ 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 = removeOldInstruction(Instruction);
+ 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]] 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 MachineOperand *Callee = Instruction.operandAt(0);
+ const MachineOperand *CallSite = Instruction.operandAt(1);
+ const MachineOperand *Mask = Instruction.operandAt(2);
+ if (Block == nullptr || Callee == nullptr || CallSite == nullptr || Mask == nullptr ||
+ Mask->kind() != MachineOperand::Kind::RegisterMask) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V call pseudo has malformed explicit operands");
+ }
+ auto Zero = physical(PhysicalRegisterID::X0);
+ auto Link = physical(PhysicalRegisterID::X1);
+ auto Scratch = physical(PhysicalRegisterID::X6);
+ if (!Zero || !Link || !Scratch)
+ return makeUnexpected(ErrorCode::InternalError, "RISC-V call registers are unavailable");
+
+ 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 : *Link;
+ 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(targetOpcode(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(targetOpcode(LowOpcode));
+ if (!LowPending)
+ return std::unexpected(LowPending.error());
+ const PhysicalRegister DestinationRegister = IsTail ? *Zero : *Link;
+ 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());
+ auto LowCallSite = LowPending->addOperand(CallSite->cloneDetached());
+ if (!LowCallSite)
+ return std::unexpected(LowCallSite.error());
+ auto LowMask = LowPending->addOperand(Mask->cloneDetached());
+ if (!LowMask)
+ return std::unexpected(LowMask.error());
+ auto Implicit = copyExtraImplicitOperands(
+ Instruction, 3, *LowPending, IsTail ? std::nullopt : std::optional<PhysicalRegister>{*Link},
+ 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 = removeOldInstruction(Instruction);
+ if (!Removed)
+ return std::unexpected(Removed.error());
+ return true;
+}
+
+[[nodiscard]] Expected<bool> expandReturn(MachineInstr &Instruction, MachineFunction &Function,
+ const TargetDescription &Target) {
+ auto Zero = physical(PhysicalRegisterID::X0);
+ auto Link = physical(PhysicalRegisterID::X1);
+ if (!Zero || !Link)
+ return makeUnexpected(ErrorCode::InternalError, "RISC-V return registers are unavailable");
+ MachineIRBuilder Builder(Function, Target);
+ auto Point = Builder.setInsertPointBefore(Instruction);
+ if (!Point)
+ return std::unexpected(Point.error());
+ auto Pending = Builder.buildInstr(targetOpcode(InstructionOpcode::JALR_RET));
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ auto Def = Pending->addDef(Register::fromPhysical(*Zero));
+ if (!Def)
+ return std::unexpected(Def.error());
+ auto Use = Pending->addUse(Register::fromPhysical(*Link));
+ if (!Use)
+ return std::unexpected(Use.error());
+ auto Immediate = Pending->addImmediate(0);
+ if (!Immediate)
+ return std::unexpected(Immediate.error());
+ auto Implicit = copyExtraImplicitOperands(Instruction, 0, *Pending, std::nullopt, *Link);
+ if (!Implicit)
+ return std::unexpected(Implicit.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,
+ 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::PostRA)
+ return false;
+ if (Instruction.opcode() == opcode(GenericOpcode::COPY))
+ return expandCopy(Instruction, Function, Target);
+ auto Subregister = expandSubregisterCopy(Instruction, Function, Target);
+ if (!Subregister || *Subregister)
+ return Subregister;
+ if (Instruction.opcode() == targetOpcode(InstructionOpcode::PseudoCALL))
+ return expandCall(Instruction, Function, Target, false);
+ if (Instruction.opcode() == targetOpcode(InstructionOpcode::PseudoTAIL))
+ return expandCall(Instruction, Function, Target, true);
+ if (Instruction.opcode() == targetOpcode(InstructionOpcode::PseudoRET))
+ return expandReturn(Instruction, Function, Target);
+ return false;
+ }
+};
+
+} // namespace
+
+const TargetPseudoExpansionInfo &pseudoExpansionInfo() noexcept {
+ static const RISCVPseudoExpansionInfo Value;
+ return Value;
+}
+
+} // namespace rcc::riscv