aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp')
-rw-r--r--backend/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp1382
1 files changed, 1382 insertions, 0 deletions
diff --git a/backend/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/backend/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
new file mode 100644
index 0000000..1e10b16
--- /dev/null
+++ b/backend/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -0,0 +1,1382 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "RISCVLegalizerInfo.h"
+
+#include "RISCVImmediateMaterialization.h"
+#include "rcc/CodeGen/GenericOpcodes.h"
+#include "rcc/CodeGen/GlobalISel/GISelChangeObserver.h"
+#include "rcc/CodeGen/GlobalISel/LegalizerHelper.h"
+#include "rcc/CodeGen/GlobalISel/Utils.h"
+#include "rcc/CodeGen/LowLevelType.h"
+#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineFrameInfo.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineIRBuilder.h"
+#include "rcc/CodeGen/MachineInstr.h"
+#include "rcc/CodeGen/MachineJumpTableInfo.h"
+#include "rcc/CodeGen/MachineMemOperand.h"
+#include "rcc/CodeGen/MachineOperand.h"
+#include "rcc/CodeGen/MachineRegisterInfo.h"
+#include "rcc/CodeGen/TargetDescription.h"
+#include "rcc/IR/DataLayout.h"
+#include "rcc/Target/RISCV/RISCVFunctionInfo.h"
+#include "rcc/Target/RISCV/RISCVSubtarget.h"
+#include "rcc/Target/RISCV/RISCVTarget.h"
+
+#include <algorithm>
+#include <array>
+#include <bit>
+#include <cstdint>
+#include <limits>
+#include <optional>
+#include <ranges>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace rcc::riscv {
+namespace {
+
+[[nodiscard]] std::optional<LowLevelType> registerType(const MachineFunction &Function,
+ Register Reg) {
+ if (!Reg.isVirtual())
+ return std::nullopt;
+ auto Info = Function.registerInfo().virtualRegisterInfo(Reg);
+ if (!Info)
+ return std::nullopt;
+ return Info->get().Type;
+}
+
+[[nodiscard]] std::optional<LowLevelType> registerType(const MachineFunction &Function,
+ const MachineOperand &Operand) {
+ const Register *Reg = Operand.getIf<Register>();
+ return Reg == nullptr ? std::nullopt : registerType(Function, *Reg);
+}
+
+[[nodiscard]] std::optional<LowLevelType> resultType(const MachineFunction &Function,
+ const MachineInstr &Instruction) {
+ const MachineOperand *Result = Instruction.operandAt(0);
+ return Result == nullptr ? std::nullopt : registerType(Function, *Result);
+}
+
+[[nodiscard]] Expected<Opcode> getRISCVWOpcode(GenericOpcode Generic) {
+ switch (Generic) {
+ case GenericOpcode::G_ASHR:
+ return instructionOpcode(InstructionOpcode::G_SRAW);
+ case GenericOpcode::G_LSHR:
+ return instructionOpcode(InstructionOpcode::G_SRLW);
+ case GenericOpcode::G_SHL:
+ return instructionOpcode(InstructionOpcode::G_SLLW);
+ case GenericOpcode::G_SDIV:
+ return instructionOpcode(InstructionOpcode::G_DIVW);
+ case GenericOpcode::G_UDIV:
+ return instructionOpcode(InstructionOpcode::G_DIVUW);
+ case GenericOpcode::G_UREM:
+ return instructionOpcode(InstructionOpcode::G_REMUW);
+ case GenericOpcode::G_ROTL:
+ return instructionOpcode(InstructionOpcode::G_ROLW);
+ case GenericOpcode::G_ROTR:
+ return instructionOpcode(InstructionOpcode::G_RORW);
+ case GenericOpcode::G_CTLZ:
+ return instructionOpcode(InstructionOpcode::G_CLZW);
+ case GenericOpcode::G_CTTZ:
+ return instructionOpcode(InstructionOpcode::G_CTZW);
+ case GenericOpcode::G_CTLS:
+ return instructionOpcode(InstructionOpcode::G_CLSW);
+ case GenericOpcode::G_FPTOSI:
+ return instructionOpcode(InstructionOpcode::G_FCVT_W_RV64);
+ case GenericOpcode::G_FPTOUI:
+ return instructionOpcode(InstructionOpcode::G_FCVT_WU_RV64);
+ default:
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "RISC-V word opcode mapping received an unexpected opcode");
+ }
+}
+
+[[nodiscard]] Expected<APInt>
+extendIntegerImmediate(const APInt &Value, std::uint32_t DestinationWidth, bool SignExtend) {
+ if (DestinationWidth == 0 || DestinationWidth > 64)
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RISC-V constant extension requires a destination no wider than 64 bits");
+
+ std::uint64_t Raw = Value.words().empty() ? 0 : Value.words().front();
+ const std::uint32_t SourceWidth = Value.bitWidth();
+ if (SignExtend && Value.isNegative() && SourceWidth < 64)
+ Raw |= ~std::uint64_t{0} << SourceWidth;
+ if (DestinationWidth < 64)
+ Raw &= (std::uint64_t{1} << DestinationWidth) - 1U;
+ return APInt::create(DestinationWidth, std::span<const std::uint64_t>(&Raw, 1));
+}
+
+[[nodiscard]] Expected<void>
+foldConstantExtension(MachineInstr &Instruction, MachineFunction &Function, GenericOpcode Generic) {
+ const MachineOperand *SourceOperand = Instruction.operandAt(1);
+ const MachineOperand *DestinationOperand = Instruction.operandAt(0);
+ const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>();
+ const Register *Destination =
+ DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>();
+ if (Source == nullptr || Destination == nullptr || !Source->isVirtual() ||
+ !Destination->isVirtual()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V constant extension requires virtual-register operands");
+ }
+ const Register SourceRegister = *Source;
+ const Register DestinationRegister = *Destination;
+ const MachineOperand *SourceDefinition = Function.registerInfo().getUniqueVRegDef(SourceRegister);
+ const MachineInstr *SourceDef =
+ SourceDefinition == nullptr ? nullptr : SourceDefinition->parent();
+ if (SourceDef == nullptr || SourceDef->opcode() != opcode(GenericOpcode::G_CONSTANT))
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RISC-V constant extension requires a single constant definition");
+ const MachineOperand *ConstantOperand = SourceDef->operandAt(1);
+ const auto *Integer =
+ ConstantOperand == nullptr ? nullptr : ConstantOperand->getIf<IntegerImmediateOperand>();
+ if (Integer == nullptr)
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RISC-V constant extension requires an integer constant");
+ const auto DestinationType = registerType(Function, *Destination);
+ if (!DestinationType)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V constant extension destination has no type");
+ const std::uint32_t DestinationWidth = DestinationType->scalarBitWidth();
+ const bool SignExtend = Generic != GenericOpcode::G_ZEXT;
+ auto Extended = extendIntegerImmediate(Integer->Value, DestinationWidth, SignExtend);
+ if (!Extended)
+ return std::unexpected(Extended.error());
+
+ if (Function.registerInfo().hasOneUse(SourceRegister)) {
+ MachineInstr *Def = const_cast<MachineInstr *>(SourceDef);
+ if (auto Replaced = Def->operandAt(0)->setRegister(DestinationRegister); !Replaced)
+ return std::unexpected(Replaced.error());
+ auto Erased = Def->eraseOperand(1);
+ if (!Erased)
+ return std::unexpected(Erased.error());
+ auto Added = Def->addOperand(MachineOperand::integerImmediate(std::move(*Extended)));
+ if (!Added)
+ return std::unexpected(Added.error());
+ return Instruction.eraseFromParent();
+ }
+
+ auto SetOpcode = Instruction.setOpcode(opcode(GenericOpcode::G_CONSTANT));
+ if (!SetOpcode)
+ return std::unexpected(SetOpcode.error());
+ while (Instruction.operandCount() > 1) {
+ auto Erased = Instruction.eraseOperand(1);
+ if (!Erased)
+ return std::unexpected(Erased.error());
+ }
+ auto Added = Instruction.addOperand(MachineOperand::integerImmediate(std::move(*Extended)));
+ if (!Added)
+ return std::unexpected(Added.error());
+
+ if (Function.registerInfo().useEmpty(SourceRegister)) {
+ MachineInstr *Def = const_cast<MachineInstr *>(SourceDef);
+ auto Removed = Def->eraseFromParent();
+ if (!Removed)
+ return std::unexpected(Removed.error());
+ // Keep the source register record as a tombstone-free historical vreg until
+ // the legalizer worklist transaction has completed.
+ }
+ return {};
+}
+
+[[nodiscard]] Expected<void> lowerConstantToPool(MachineInstr &Instruction,
+ MachineFunction &Function,
+ const TargetDescription &Target) {
+ const MachineOperand *DestinationOperand = Instruction.operandAt(0);
+ const Register *Destination =
+ DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>();
+ const MachineOperand *ImmediateOperand = Instruction.operandAt(1);
+ const auto *Immediate =
+ ImmediateOperand == nullptr ? nullptr : ImmediateOperand->getIf<IntegerImmediateOperand>();
+ if (Destination == nullptr || !Destination->isVirtual() || Immediate == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V constant-pool legalization has invalid operands");
+ const auto DestinationType = registerType(Function, *Destination);
+ if (!DestinationType || DestinationType->scalarBitWidth() != 64)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V constant-pool legalization requires a 64-bit destination");
+ auto Value = GenericConstantData::integer(Immediate->Value);
+ if (!Value)
+ return std::unexpected(Value.error());
+ auto Align = Alignment::create(8);
+ if (!Align)
+ return std::unexpected(Align.error());
+ MachineConstantPoolEntryDesc Description;
+ Description.Value = std::move(*Value);
+ Description.Size = 8;
+ Description.Align = *Align;
+ auto PoolIndex = Function.constantPool().addEntry(std::move(Description));
+ if (!PoolIndex)
+ return std::unexpected(PoolIndex.error());
+
+ auto PointerType = LowLevelType::makePointer(0, 64);
+ if (!PointerType)
+ return std::unexpected(PointerType.error());
+ auto Address = Function.registerInfo().createVirtualRegister(*PointerType);
+ if (!Address)
+ return std::unexpected(Address.error());
+ MachineIRBuilder Builder(Function, Target);
+ auto Point = Builder.setInsertPointBefore(Instruction);
+ if (!Point)
+ return std::unexpected(Point.error());
+ auto AddressInstruction = Builder.buildInstr(opcode(GenericOpcode::G_CONSTANT_POOL));
+ if (!AddressInstruction)
+ return std::unexpected(AddressInstruction.error());
+ auto AddressDef = AddressInstruction->addDef(*Address);
+ if (!AddressDef)
+ return std::unexpected(AddressDef.error());
+ auto AddressPool = AddressInstruction->addConstantPool(*PoolIndex);
+ if (!AddressPool)
+ return std::unexpected(AddressPool.error());
+ auto BuiltAddress = AddressInstruction->build();
+ if (!BuiltAddress)
+ return std::unexpected(BuiltAddress.error());
+
+ MachineMemOperandDesc MemoryDescription;
+ MemoryDescription.Accesses = {MemoryAccessFlag::Load};
+ MemoryDescription.Size = *MemorySize::fixed(8);
+ MemoryDescription.MemoryType = *DestinationType;
+ MemoryDescription.Align = *Align;
+ MemoryDescription.Base = MemoryBase::constantPoolPseudo();
+ auto Memory = Function.createMemOperand(std::move(MemoryDescription));
+ if (!Memory)
+ return std::unexpected(Memory.error());
+ auto SetOpcode = Instruction.setOpcode(opcode(GenericOpcode::G_LOAD));
+ if (!SetOpcode)
+ return std::unexpected(SetOpcode.error());
+ while (Instruction.operandCount() > 1) {
+ auto Erased = Instruction.eraseOperand(1);
+ if (!Erased)
+ return std::unexpected(Erased.error());
+ }
+ auto AddressUse =
+ MachineOperand::registerOperand(*Address, RegisterOperandFlags{RegisterOperandFlag::Use});
+ if (!AddressUse)
+ return std::unexpected(AddressUse.error());
+ auto AddedAddress = Instruction.addOperand(std::move(*AddressUse));
+ if (!AddedAddress)
+ return std::unexpected(AddedAddress.error());
+ auto AddedMemory = Instruction.addMemOperand(**Memory);
+ if (!AddedMemory)
+ return std::unexpected(AddedMemory.error());
+ return {};
+}
+
+[[nodiscard]] Expected<std::int64_t> signedImmediate(const MachineOperand &Operand) {
+ if (const std::int64_t *Value = Operand.getIf<std::int64_t>())
+ return *Value;
+ const auto *Integer = Operand.getIf<IntegerImmediateOperand>();
+ if (Integer == nullptr || Integer->Value.bitWidth() == 0 || Integer->Value.bitWidth() > 64)
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RISC-V constant selection requires an immediate no wider than 64 bits");
+ std::uint64_t Raw = Integer->Value.words().empty() ? 0 : Integer->Value.words().front();
+ const std::uint32_t Width = Integer->Value.bitWidth();
+ if (Width < 64 && (Raw & (std::uint64_t{1} << (Width - 1U))) != 0)
+ Raw |= ~std::uint64_t{0} << Width;
+ return std::bit_cast<std::int64_t>(Raw);
+}
+
+} // namespace
+
+RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &Subtarget) : Subtarget(Subtarget) {
+ const LowLevelType S1 = *LowLevelType::makeCategoryNeutralScalar(1);
+ const LowLevelType S8 = *LowLevelType::makeCategoryNeutralScalar(8);
+ const LowLevelType S16 = *LowLevelType::makeCategoryNeutralScalar(16);
+ const LowLevelType S32 = *LowLevelType::makeCategoryNeutralScalar(32);
+ const LowLevelType S64 = *LowLevelType::makeCategoryNeutralScalar(64);
+ const LowLevelType S128 = *LowLevelType::makeCategoryNeutralScalar(128);
+ const LowLevelType P0 = *LowLevelType::makePointer(0, 64);
+ const bool HasM = Subtarget.features().has(RISCVExtension::M);
+ const bool HasA = Subtarget.features().has(RISCVExtension::A);
+ const bool HasF = Subtarget.features().has(RISCVExtension::F);
+ const bool HasD = Subtarget.features().has(RISCVExtension::D);
+ const bool HasZbb = Subtarget.features().has(RISCVExtension::Zbb);
+
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_ASSERT_SEXT),
+ opcode(GenericOpcode::G_ASSERT_ZEXT),
+ opcode(GenericOpcode::G_ASSERT_ALIGN)})
+ .alwaysLegal();
+
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_ADD), opcode(GenericOpcode::G_SUB)})
+ .legalFor({S64})
+ .customFor({S32})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, S64, S64);
+
+ getActionDefinitionsBuilder(
+ {opcode(GenericOpcode::G_AND), opcode(GenericOpcode::G_OR), opcode(GenericOpcode::G_XOR)})
+ .legalFor({S64})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, S64, S64);
+
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_UADDE), opcode(GenericOpcode::G_UADDO),
+ opcode(GenericOpcode::G_USUBE), opcode(GenericOpcode::G_USUBO),
+ opcode(GenericOpcode::G_READ_REGISTER),
+ opcode(GenericOpcode::G_WRITE_REGISTER)})
+ .lower();
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_SADDE), opcode(GenericOpcode::G_SADDO),
+ opcode(GenericOpcode::G_SSUBE), opcode(GenericOpcode::G_SSUBO)})
+ .minScalar(0, S64)
+ .lower();
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_UADDSAT), opcode(GenericOpcode::G_SADDSAT),
+ opcode(GenericOpcode::G_USUBSAT), opcode(GenericOpcode::G_SSUBSAT),
+ opcode(GenericOpcode::G_SSHLSAT), opcode(GenericOpcode::G_USHLSAT)})
+ .lower();
+
+ getActionDefinitionsBuilder(
+ {opcode(GenericOpcode::G_SHL), opcode(GenericOpcode::G_ASHR), opcode(GenericOpcode::G_LSHR)})
+ .legalFor({{S64, S64}})
+ .customFor({{S32, S32}})
+ .widenScalarToNextPow2(0)
+ .clampScalar(1, S64, S64)
+ .clampScalar(0, S64, S64);
+
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_ZEXT), opcode(GenericOpcode::G_SEXT),
+ opcode(GenericOpcode::G_ANYEXT)})
+ .legalFor({{S32, S16}, {S64, S16}, {S64, S32}})
+ .maxScalar(0, S64);
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_TRUNC)).alwaysLegal();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_SEXT_INREG))
+ .customFor({S64})
+ .clampScalar(0, S64, S64)
+ .lower();
+
+ for (const GenericOpcode Generic :
+ {GenericOpcode::G_MERGE_VALUES, GenericOpcode::G_UNMERGE_VALUES}) {
+ const std::size_t BigTypeIndex = Generic == GenericOpcode::G_MERGE_VALUES ? 0 : 1;
+ const std::size_t LittleTypeIndex = Generic == GenericOpcode::G_MERGE_VALUES ? 1 : 0;
+ getActionDefinitionsBuilder(opcode(Generic))
+ .widenScalarToNextPow2(LittleTypeIndex, 64)
+ .widenScalarToNextPow2(BigTypeIndex, 64)
+ .clampScalar(LittleTypeIndex, S64, S64)
+ .clampScalar(BigTypeIndex, S64, S64);
+ }
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_FSHL), opcode(GenericOpcode::G_FSHR)})
+ .lower();
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_ROTR), opcode(GenericOpcode::G_ROTL)})
+ .legalFor(HasZbb, {{S64, S64}})
+ .customFor(HasZbb, {{S32, S32}})
+ .lower();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_BITREVERSE)).maxScalar(0, S64).lower();
+ auto &ByteSwapActions = getActionDefinitionsBuilder(opcode(GenericOpcode::G_BSWAP));
+ if (HasZbb)
+ ByteSwapActions.legalFor({S64}).clampScalar(0, S64, S64);
+ else
+ ByteSwapActions.maxScalar(0, S64).lower();
+
+ auto &CountZerosActions =
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_CTLZ), opcode(GenericOpcode::G_CTTZ)});
+ auto &CountZerosPoisonActions = getActionDefinitionsBuilder(
+ {opcode(GenericOpcode::G_CTLZ_ZERO_POISON), opcode(GenericOpcode::G_CTTZ_ZERO_POISON)});
+ if (HasZbb) {
+ CountZerosActions.legalFor({{S64, S64}})
+ .customFor({{S32, S32}})
+ .clampScalar(0, S32, S64)
+ .widenScalarToNextPow2(0)
+ .scalarSameSizeAs(1, 0);
+ } else {
+ CountZerosActions.maxScalar(0, S64).scalarSameSizeAs(1, 0).lower();
+ CountZerosPoisonActions.maxScalar(0, S64).scalarSameSizeAs(1, 0);
+ }
+ CountZerosPoisonActions.lower();
+
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_CTLS))
+ .maxScalar(0, S64)
+ .scalarSameSizeAs(1, 0)
+ .lower();
+
+ auto &CountPopulationActions = getActionDefinitionsBuilder(opcode(GenericOpcode::G_CTPOP));
+ if (HasZbb) {
+ CountPopulationActions.legalFor({{S64, S64}}).clampScalar(0, S64, S64).scalarSameSizeAs(1, 0);
+ } else {
+ CountPopulationActions.widenScalarToNextPow2(0, 8)
+ .clampScalar(0, S8, S64)
+ .scalarSameSizeAs(1, 0)
+ .lower();
+ }
+
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_CONSTANT))
+ .legalFor({P0})
+ .customFor({S64})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, S64, S64);
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FREEZE))
+ .legalFor({S16, S32, S64, P0})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, S16, S64);
+ getActionDefinitionsBuilder(
+ {opcode(GenericOpcode::G_IMPLICIT_DEF), opcode(GenericOpcode::G_CONSTANT_FOLD_BARRIER)})
+ .legalFor({S32, S64, P0})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, S32, S64);
+
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_ICMP))
+ .legalFor({{S64, S64}, {S64, P0}})
+ .widenScalarToNextPow2(1, 8)
+ .clampScalar(1, S64, S64)
+ .clampScalar(0, S64, S64);
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_SELECT))
+ .legalFor({{S32, S64}, {S64, S64}, {P0, S64}})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, S32, S64)
+ .clampScalar(1, S64, S64);
+
+ auto &LoadRules = getActionDefinitionsBuilder(opcode(GenericOpcode::G_LOAD));
+ auto &StoreRules = getActionDefinitionsBuilder(opcode(GenericOpcode::G_STORE));
+ auto &ExtLoadRules = getActionDefinitionsBuilder(
+ {opcode(GenericOpcode::G_SEXTLOAD), opcode(GenericOpcode::G_ZEXTLOAD)});
+ const auto GetScalarMemAlign = [&Subtarget](std::uint64_t Size) {
+ return Subtarget.features().has(RISCVSubtargetOption::UnalignedScalarMemory) ? 8 : Size;
+ };
+ LoadRules
+ .legalForTypesWithMemDesc({{S16, P0, S8, GetScalarMemAlign(8)},
+ {S32, P0, S8, GetScalarMemAlign(8)},
+ {S16, P0, S16, GetScalarMemAlign(16)},
+ {S32, P0, S16, GetScalarMemAlign(16)},
+ {S32, P0, S32, GetScalarMemAlign(32)},
+ {S64, P0, S8, GetScalarMemAlign(8)},
+ {S64, P0, S16, GetScalarMemAlign(16)},
+ {S64, P0, S32, GetScalarMemAlign(32)},
+ {S64, P0, S64, GetScalarMemAlign(64)},
+ {P0, P0, S64, GetScalarMemAlign(64)}})
+ .widenScalarToNextPow2(0, 8)
+ .lowerIfMemSizeNotByteSizePow2()
+ .clampScalar(0, S16, S64)
+ .lower();
+ StoreRules
+ .legalForTypesWithMemDesc({{S16, P0, S8, GetScalarMemAlign(8)},
+ {S32, P0, S8, GetScalarMemAlign(8)},
+ {S16, P0, S16, GetScalarMemAlign(16)},
+ {S32, P0, S16, GetScalarMemAlign(16)},
+ {S32, P0, S32, GetScalarMemAlign(32)},
+ {S64, P0, S8, GetScalarMemAlign(8)},
+ {S64, P0, S16, GetScalarMemAlign(16)},
+ {S64, P0, S32, GetScalarMemAlign(32)},
+ {S64, P0, S64, GetScalarMemAlign(64)},
+ {P0, P0, S64, GetScalarMemAlign(64)}})
+ .clampScalar(0, S16, S64)
+ .lowerIfMemSizeNotByteSizePow2()
+ .lower();
+ ExtLoadRules
+ .legalForTypesWithMemDesc({{S64, P0, S8, GetScalarMemAlign(8)},
+ {S64, P0, S16, GetScalarMemAlign(16)},
+ {S64, P0, S32, GetScalarMemAlign(32)}})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, S64, S64)
+ .lower();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FPEXTLOAD))
+ .legalForTypesWithMemDesc({{S64, P0, S32, GetScalarMemAlign(32)}})
+ .lower();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FPTRUNCSTORE))
+ .legalForTypesWithMemDesc({{S64, P0, S32, GetScalarMemAlign(32)}})
+ .lower();
+
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_PTR_ADD), opcode(GenericOpcode::G_PTRMASK)})
+ .legalFor({{P0, S64}});
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_PTRTOINT))
+ .legalFor({{S64, P0}})
+ .clampScalar(0, S64, S64);
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_INTTOPTR))
+ .legalFor({{P0, S64}})
+ .clampScalar(1, S64, S64);
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_BR)).alwaysLegal();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_BRCOND)).legalFor({S64}).minScalar(0, S64);
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_BRJT)).customFor({{P0, S64}});
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_BRINDIRECT)).legalFor({P0});
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_PHI))
+ .legalFor({P0, S32, S64})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, S32, S64);
+ getActionDefinitionsBuilder(
+ {opcode(GenericOpcode::G_GLOBAL_VALUE), opcode(GenericOpcode::G_JUMP_TABLE),
+ opcode(GenericOpcode::G_CONSTANT_POOL), opcode(GenericOpcode::G_FRAME_INDEX)})
+ .legalFor({P0});
+
+ if (HasM) {
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_MUL))
+ .legalFor({S64})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, S64, S64);
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_SMULH), opcode(GenericOpcode::G_UMULH)})
+ .legalFor({S64})
+ .lower();
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_SMULO), opcode(GenericOpcode::G_UMULO)})
+ .minScalar(0, S64)
+ .lower();
+ } else {
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_MUL))
+ .libcallFor({S64, S128})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, S64, S128);
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_SMULH), opcode(GenericOpcode::G_UMULH)})
+ .lowerFor({S64});
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_SMULO), opcode(GenericOpcode::G_UMULO)})
+ .minScalar(0, S64)
+ // Widen s64 to s128 so one libcall provides both the low result and
+ // the high half needed for the overflow check.
+ .widenScalarIf(
+ [S64](const LegalizationQuery &Query) {
+ return !Query.Types.empty() && Query.Types[0] == S64;
+ },
+ [S128](const LegalizationQuery &) { return std::pair{std::size_t{0}, S128}; })
+ .lower();
+ }
+ if (HasM) {
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_SDIV), opcode(GenericOpcode::G_UDIV),
+ opcode(GenericOpcode::G_UREM)})
+ .legalFor({S64})
+ .customFor({S32})
+ .libcallFor({S128})
+ .clampScalar(0, S32, S128)
+ .widenScalarToNextPow2(0);
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_SREM))
+ .legalFor({S64})
+ .libcallFor({S128})
+ .clampScalar(0, S64, S128)
+ .widenScalarToNextPow2(0);
+ } else {
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_SDIV), opcode(GenericOpcode::G_UDIV),
+ opcode(GenericOpcode::G_SREM), opcode(GenericOpcode::G_UREM)})
+ .libcallFor({S64, S128})
+ .clampScalar(0, S64, S128)
+ .widenScalarToNextPow2(0);
+ }
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_SDIVREM), opcode(GenericOpcode::G_UDIVREM)})
+ .lower();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_ABS)).lower();
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_ABDS), opcode(GenericOpcode::G_ABDU)})
+ .minScalar(HasZbb, 0, S64)
+ .lower();
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_UMAX), opcode(GenericOpcode::G_UMIN),
+ opcode(GenericOpcode::G_SMAX), opcode(GenericOpcode::G_SMIN)})
+ .legalFor(HasZbb, {S64})
+ .minScalar(HasZbb, 0, S64)
+ .lower();
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_SCMP), opcode(GenericOpcode::G_UCMP)})
+ .lower();
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_MEMCPY), opcode(GenericOpcode::G_MEMMOVE),
+ opcode(GenericOpcode::G_MEMSET)})
+ .libcall();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_MEMCPY_INLINE)).lower();
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_DYN_STACKALLOC),
+ opcode(GenericOpcode::G_STACKSAVE),
+ opcode(GenericOpcode::G_STACKRESTORE)})
+ .lower();
+
+ getActionDefinitionsBuilder(
+ {opcode(GenericOpcode::G_FADD), opcode(GenericOpcode::G_FSUB), opcode(GenericOpcode::G_FMUL),
+ opcode(GenericOpcode::G_FDIV), opcode(GenericOpcode::G_FMA), opcode(GenericOpcode::G_FSQRT),
+ opcode(GenericOpcode::G_FMAXNUM), opcode(GenericOpcode::G_FMINNUM),
+ opcode(GenericOpcode::G_FMAXIMUMNUM), opcode(GenericOpcode::G_FMINIMUMNUM)})
+ .legalFor(HasF, {S32})
+ .legalFor(HasD, {S64})
+ .libcallFor({S32, S64})
+ .libcallFor({S128});
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_FNEG), opcode(GenericOpcode::G_FABS)})
+ .legalFor(HasF, {S32})
+ .legalFor(HasD, {S64})
+ .lowerFor({S32, S64, S128});
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FREM))
+ .libcallFor({S32, S64, S128})
+ .minScalar(0, S32)
+ .scalarize(0);
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FCOPYSIGN))
+ .legalFor(HasF, {{S32, S32}})
+ .legalFor(HasD, {{S64, S64}, {S32, S64}, {S64, S32}})
+ .lower();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FPTRUNC))
+ .legalFor(HasD, {{S32, S64}})
+ .libcallFor({{S32, S64}, {S32, S128}, {S64, S128}});
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FPEXT))
+ .legalFor(HasD, {{S64, S32}})
+ .libcallFor({{S64, S32}, {S128, S32}, {S128, S64}});
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FCMP))
+ .legalFor(HasF, {{S64, S32}})
+ .legalFor(HasD, {{S64, S64}})
+ .clampScalar(0, S64, S64)
+ .libcallFor({{S64, S32}, {S64, S64}, {S64, S128}});
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_IS_FPCLASS))
+ .customFor(HasF, {{S1, S32}})
+ .customFor(HasD, {{S1, S64}})
+ .lower();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FCONSTANT))
+ .legalFor(HasF, {S32})
+ .legalFor(HasD, {S64})
+ .customFor({S32, S64})
+ .lowerFor({S64, S128});
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_FPTOSI), opcode(GenericOpcode::G_FPTOUI)})
+ .legalFor(HasF, {{S64, S32}})
+ .legalFor(HasD, {{S64, S64}})
+ .customFor(HasF, {{S32, S32}})
+ .customFor(HasD, {{S32, S64}})
+ .widenScalarToNextPow2(0)
+ .minScalar(0, S32)
+ .libcallFor({{S32, S32},
+ {S64, S32},
+ {S32, S64},
+ {S64, S64},
+ {S32, S128},
+ {S64, S128},
+ {S128, S32},
+ {S128, S64},
+ {S128, S128}});
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_SITOFP), opcode(GenericOpcode::G_UITOFP)})
+ .legalFor(HasF, {{S32, S64}})
+ .legalFor(HasD, {{S64, S64}})
+ .widenScalarToNextPow2(1)
+ .widenScalarIf(
+ [HasF, HasD](const LegalizationQuery &Query) {
+ if (Query.Types.size() < 2 || !Query.Types[0].isCategoryNeutralScalar() ||
+ !Query.Types[1].isCategoryNeutralScalar() ||
+ Query.Types[1].scalarBitWidth() >= 64) {
+ return false;
+ }
+ return (HasF && Query.Types[0].scalarBitWidth() == 32) ||
+ (HasD && Query.Types[0].scalarBitWidth() == 64);
+ },
+ [S64](const LegalizationQuery &) { return std::pair{std::size_t{1}, S64}; })
+ .minScalar(1, S32)
+ .libcallFor({{S32, S32},
+ {S64, S32},
+ {S32, S64},
+ {S64, S64},
+ {S128, S32},
+ {S128, S64},
+ {S32, S128},
+ {S64, S128},
+ {S128, S128}});
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_FCEIL), opcode(GenericOpcode::G_FFLOOR),
+ opcode(GenericOpcode::G_FRINT), opcode(GenericOpcode::G_FNEARBYINT),
+ opcode(GenericOpcode::G_INTRINSIC_TRUNC),
+ opcode(GenericOpcode::G_INTRINSIC_ROUND),
+ opcode(GenericOpcode::G_INTRINSIC_ROUNDEVEN)})
+ .libcallFor({S32, S64, S128});
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_FCOS), opcode(GenericOpcode::G_FSIN),
+ opcode(GenericOpcode::G_FTAN), opcode(GenericOpcode::G_FPOW),
+ opcode(GenericOpcode::G_FLOG), opcode(GenericOpcode::G_FLOG2),
+ opcode(GenericOpcode::G_FLOG10), opcode(GenericOpcode::G_FEXP),
+ opcode(GenericOpcode::G_FEXP2), opcode(GenericOpcode::G_FEXP10),
+ opcode(GenericOpcode::G_FACOS), opcode(GenericOpcode::G_FASIN),
+ opcode(GenericOpcode::G_FATAN), opcode(GenericOpcode::G_FATAN2),
+ opcode(GenericOpcode::G_FCOSH), opcode(GenericOpcode::G_FSINH),
+ opcode(GenericOpcode::G_FTANH), opcode(GenericOpcode::G_FMODF)})
+ .libcallFor({S32, S64, S128});
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_FPOWI), opcode(GenericOpcode::G_FLDEXP)})
+ .libcallFor({{S32, S32}, {S64, S32}, {S128, S32}});
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FCANONICALIZE))
+ .legalFor(HasF, {S32})
+ .legalFor(HasD, {S64});
+
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_VASTART)).customFor({P0});
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_VAARG)).clampScalar(0, S64, S64).lower();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_ATOMIC_CMPXCHG_WITH_SUCCESS))
+ .lowerIf([S8, S16, S32, S64, P0](const LegalizationQuery &Query) {
+ return Query.Types.size() >= 3 &&
+ (Query.Types[0] == S8 || Query.Types[0] == S16 || Query.Types[0] == S32 ||
+ Query.Types[0] == S64) &&
+ Query.Types[2] == P0;
+ });
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_ATOMIC_CMPXCHG))
+ .legalFor(HasA, {{S64, P0}})
+ .libcallFor(!HasA, {{S8, P0}, {S16, P0}, {S32, P0}, {S64, P0}})
+ .clampScalar(0, S64, S64);
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_ATOMICRMW_ADD))
+ .legalFor(HasA, {{S64, P0}})
+ .libcallFor(!HasA, {{S8, P0}, {S16, P0}, {S32, P0}, {S64, P0}})
+ .clampScalar(0, S64, S64);
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_ATOMICRMW_SUB))
+ .libcallFor(!HasA, {{S8, P0}, {S16, P0}, {S32, P0}, {S64, P0}})
+ .clampScalar(0, S64, S64)
+ .lower();
+ getActionDefinitionsBuilder(
+ {opcode(GenericOpcode::G_ATOMICRMW_XCHG), opcode(GenericOpcode::G_ATOMICRMW_AND),
+ opcode(GenericOpcode::G_ATOMICRMW_OR), opcode(GenericOpcode::G_ATOMICRMW_XOR),
+ opcode(GenericOpcode::G_ATOMICRMW_MAX), opcode(GenericOpcode::G_ATOMICRMW_MIN),
+ opcode(GenericOpcode::G_ATOMICRMW_UMAX), opcode(GenericOpcode::G_ATOMICRMW_UMIN)})
+ .legalFor(HasA, {{S64, P0}})
+ .libcallFor(!HasA, {{S8, P0}, {S16, P0}, {S32, P0}, {S64, P0}})
+ .clampScalar(0, S64, S64);
+ getActionDefinitionsBuilder(
+ {opcode(GenericOpcode::G_INTRINSIC), opcode(GenericOpcode::G_INTRINSIC_W_SIDE_EFFECTS)})
+ .alwaysLegal();
+ getActionDefinitionsBuilder(opcode(GenericOpcode::G_FENCE)).alwaysLegal();
+ getActionDefinitionsBuilder({opcode(GenericOpcode::G_TRAP), opcode(GenericOpcode::G_DEBUGTRAP),
+ opcode(GenericOpcode::G_UBSANTRAP)})
+ .alwaysLegal();
+}
+
+Expected<void> RISCVLegalizerInfo::legalizeBRJT(MachineInstr &Instruction,
+ MachineIRBuilder &MIRBuilder) const {
+ if (Instruction.operandCount() != 3)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_BRJT requires table pointer, table index, and index operands");
+
+ const MachineOperand *TablePointerOperand = Instruction.operandAt(0);
+ const MachineOperand *TableIndexOperand = Instruction.operandAt(1);
+ const MachineOperand *IndexOperand = Instruction.operandAt(2);
+ const Register *TablePointer =
+ TablePointerOperand == nullptr ? nullptr : TablePointerOperand->getIf<Register>();
+ const JumpTableOperand *TableIndex =
+ TableIndexOperand == nullptr ? nullptr : TableIndexOperand->getIf<JumpTableOperand>();
+ const Register *Index = IndexOperand == nullptr ? nullptr : IndexOperand->getIf<Register>();
+ if (TablePointer == nullptr || TableIndex == nullptr || Index == nullptr ||
+ !TablePointer->isVirtual() || !Index->isVirtual()) {
+ return makeUnexpected(
+ ErrorCode::VerificationError,
+ "RISC-V G_BRJT requires virtual-register table pointer and index operands");
+ }
+
+ MachineFunction &Function = MIRBuilder.function();
+ const MachineJumpTableEntry *TableEntry = Function.jumpTableInfo().table(TableIndex->Index);
+ const MachineJumpTableDesc *Table = TableEntry == nullptr ? nullptr : TableEntry->description();
+ if (Table == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_BRJT references an undefined jump table");
+ }
+ if (!std::has_single_bit(Table->EntrySize)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_BRJT requires a power-of-two jump table entry size");
+ }
+
+ auto PointerType = Function.registerInfo().getType(*TablePointer);
+ if (!PointerType)
+ return std::unexpected(PointerType.error());
+ auto IndexType = Function.registerInfo().getType(*Index);
+ if (!IndexType)
+ return std::unexpected(IndexType.error());
+ if (!PointerType->isPointer() || !IndexType->isCategoryNeutralScalar()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_BRJT requires scalar pointer and integer index types");
+ }
+ const std::optional<std::uint32_t> AddressSpace = PointerType->addressSpace();
+ if (!AddressSpace) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_BRJT table pointer has no address space");
+ }
+
+ auto EntryType = LowLevelType::makeCategoryNeutralScalar(
+ static_cast<std::uint64_t>(Table->EntrySize) * std::uint64_t{8});
+ if (!EntryType)
+ return std::unexpected(EntryType.error());
+ auto EntryMemorySize = MemorySize::fixed(Table->EntrySize);
+ if (!EntryMemorySize)
+ return std::unexpected(EntryMemorySize.error());
+
+ MachineMemOperandDesc LoadDescription;
+ LoadDescription.Accesses = {MemoryAccessFlag::Load};
+ LoadDescription.Size = *EntryMemorySize;
+ LoadDescription.MemoryType = *EntryType;
+ LoadDescription.Align = Table->Align;
+ LoadDescription.AddressSpace = *AddressSpace;
+ LoadDescription.Base = MemoryBase::jumpTablePseudo();
+ auto LoadMemory = Function.createMemOperand(std::move(LoadDescription));
+ if (!LoadMemory)
+ return std::unexpected(LoadMemory.error());
+
+ auto ShiftAmount = Function.registerInfo().createVirtualRegister(*IndexType);
+ if (!ShiftAmount)
+ return std::unexpected(ShiftAmount.error());
+ if (auto Built = MIRBuilder.buildConstant(
+ *ShiftAmount,
+ static_cast<std::int64_t>(std::countr_zero(static_cast<unsigned int>(Table->EntrySize))));
+ !Built) {
+ return std::unexpected(Built.error());
+ }
+
+ auto ScaledIndex = Function.registerInfo().createVirtualRegister(*IndexType);
+ if (!ScaledIndex)
+ return std::unexpected(ScaledIndex.error());
+ if (auto Built = MIRBuilder.buildShl(*ScaledIndex, *Index, *ShiftAmount); !Built)
+ return std::unexpected(Built.error());
+
+ auto EntryAddress = Function.registerInfo().createVirtualRegister(*PointerType);
+ if (!EntryAddress)
+ return std::unexpected(EntryAddress.error());
+ auto AddressBuilder = MIRBuilder.buildInstr(opcode(GenericOpcode::G_PTR_ADD));
+ if (!AddressBuilder)
+ return std::unexpected(AddressBuilder.error());
+ if (auto Added = AddressBuilder->addDef(*EntryAddress); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = AddressBuilder->addUse(*TablePointer); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = AddressBuilder->addUse(*ScaledIndex); !Added)
+ return std::unexpected(Added.error());
+ if (auto Built = AddressBuilder->build(); !Built)
+ return std::unexpected(Built.error());
+
+ Register Target;
+ switch (Table->Encoding) {
+ case JumpTableEntryEncoding::Custom32: {
+ auto LoadedOffset = Function.registerInfo().createVirtualRegister(*IndexType);
+ if (!LoadedOffset)
+ return std::unexpected(LoadedOffset.error());
+ auto LoadBuilder = MIRBuilder.buildInstr(opcode(GenericOpcode::G_SEXTLOAD));
+ if (!LoadBuilder)
+ return std::unexpected(LoadBuilder.error());
+ if (auto Added = LoadBuilder->addDef(*LoadedOffset); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = LoadBuilder->addUse(*EntryAddress); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = LoadBuilder->addMemOperand(**LoadMemory); !Added)
+ return std::unexpected(Added.error());
+ if (auto Built = LoadBuilder->build(); !Built)
+ return std::unexpected(Built.error());
+
+ auto TargetPointer = Function.registerInfo().createVirtualRegister(*PointerType);
+ if (!TargetPointer)
+ return std::unexpected(TargetPointer.error());
+ if (auto Built = MIRBuilder.buildIntToPtr(*TargetPointer, *LoadedOffset); !Built)
+ return std::unexpected(Built.error());
+ Target = *TargetPointer;
+ break;
+ }
+ case JumpTableEntryEncoding::LabelDifference32: {
+ auto LoadedOffset = Function.registerInfo().createVirtualRegister(*IndexType);
+ if (!LoadedOffset)
+ return std::unexpected(LoadedOffset.error());
+ auto LoadBuilder = MIRBuilder.buildInstr(opcode(GenericOpcode::G_SEXTLOAD));
+ if (!LoadBuilder)
+ return std::unexpected(LoadBuilder.error());
+ if (auto Added = LoadBuilder->addDef(*LoadedOffset); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = LoadBuilder->addUse(*EntryAddress); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = LoadBuilder->addMemOperand(**LoadMemory); !Added)
+ return std::unexpected(Added.error());
+ if (auto Built = LoadBuilder->build(); !Built)
+ return std::unexpected(Built.error());
+
+ auto TargetPointer = Function.registerInfo().createVirtualRegister(*PointerType);
+ if (!TargetPointer)
+ return std::unexpected(TargetPointer.error());
+ auto TargetBuilder = MIRBuilder.buildInstr(opcode(GenericOpcode::G_PTR_ADD));
+ if (!TargetBuilder)
+ return std::unexpected(TargetBuilder.error());
+ if (auto Added = TargetBuilder->addDef(*TargetPointer); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = TargetBuilder->addUse(*TablePointer); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = TargetBuilder->addUse(*LoadedOffset); !Added)
+ return std::unexpected(Added.error());
+ if (auto Built = TargetBuilder->build(); !Built)
+ return std::unexpected(Built.error());
+ Target = *TargetPointer;
+ break;
+ }
+ case JumpTableEntryEncoding::BlockAddress: {
+ auto TargetPointer = Function.registerInfo().createVirtualRegister(*PointerType);
+ if (!TargetPointer)
+ return std::unexpected(TargetPointer.error());
+ auto LoadBuilder = MIRBuilder.buildInstr(opcode(GenericOpcode::G_LOAD));
+ if (!LoadBuilder)
+ return std::unexpected(LoadBuilder.error());
+ if (auto Added = LoadBuilder->addDef(*TargetPointer); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = LoadBuilder->addUse(*EntryAddress); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = LoadBuilder->addMemOperand(**LoadMemory); !Added)
+ return std::unexpected(Added.error());
+ if (auto Built = LoadBuilder->build(); !Built)
+ return std::unexpected(Built.error());
+ Target = *TargetPointer;
+ break;
+ }
+ default:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RISC-V G_BRJT has an unsupported jump table encoding");
+ }
+
+ auto BranchBuilder = MIRBuilder.buildInstr(opcode(GenericOpcode::G_BRINDIRECT));
+ if (!BranchBuilder)
+ return std::unexpected(BranchBuilder.error());
+ if (auto Added = BranchBuilder->addUse(Target); !Added)
+ return std::unexpected(Added.error());
+ if (auto Built = BranchBuilder->build(); !Built)
+ return std::unexpected(Built.error());
+
+ return Instruction.eraseFromParent();
+}
+
+Expected<void> RISCVLegalizerInfo::legalizeVAStart(MachineInstr &Instruction,
+ MachineIRBuilder &MIRBuilder) const {
+ if (Instruction.operandCount() != 1 || Instruction.memOperands().size() != 1 ||
+ Instruction.memOperands().front() == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_VASTART requires one pointer operand and one memory operand");
+ }
+
+ MachineFunction &Function = MIRBuilder.function();
+ const RISCVFunctionInfo *FunctionInfo = Function.getTargetInfo<RISCVFunctionInfo>();
+ if (FunctionInfo == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_VASTART requires RISC-V function information");
+
+ const MachineOperand *DestinationOperand = Instruction.operandAt(0);
+ const Register *Destination =
+ DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>();
+ if (Destination == nullptr || !Destination->isVirtual()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_VASTART requires a virtual-register pointer operand");
+ }
+ auto AddressType = Function.registerInfo().getType(*Destination);
+ if (!AddressType)
+ return std::unexpected(AddressType.error());
+
+ const std::int64_t LLVMFrameIndex = FunctionInfo->varArgsFrameIndex();
+ const bool IsFixedObject = LLVMFrameIndex < 0;
+ const std::uint64_t ObjectOrdinal = IsFixedObject
+ ? static_cast<std::uint64_t>(-LLVMFrameIndex - 1)
+ : static_cast<std::uint64_t>(LLVMFrameIndex);
+ std::optional<FrameIndex> VarArgsFrameIndex;
+ std::uint64_t CurrentOrdinal = 0;
+ for (const StackObject &Object : Function.frameInfo().objects()) {
+ if (Object.isTombstone() || Object.isFixed() != IsFixedObject)
+ continue;
+ if (CurrentOrdinal == ObjectOrdinal) {
+ VarArgsFrameIndex = Object.index();
+ break;
+ }
+ ++CurrentOrdinal;
+ }
+ if (!VarArgsFrameIndex) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_VASTART references a missing varargs frame object");
+ }
+
+ auto VarArgsAddress = Function.registerInfo().createVirtualRegister(*AddressType);
+ if (!VarArgsAddress)
+ return std::unexpected(VarArgsAddress.error());
+ if (auto Point = MIRBuilder.setInsertPointBefore(Instruction); !Point)
+ return std::unexpected(Point.error());
+
+ auto FrameIndexBuilder = MIRBuilder.buildInstr(opcode(GenericOpcode::G_FRAME_INDEX));
+ if (!FrameIndexBuilder)
+ return std::unexpected(FrameIndexBuilder.error());
+ if (auto Added = FrameIndexBuilder->addDef(*VarArgsAddress); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = FrameIndexBuilder->addFrameIndex(*VarArgsFrameIndex); !Added)
+ return std::unexpected(Added.error());
+ if (auto Built = FrameIndexBuilder->build(); !Built)
+ return std::unexpected(Built.error());
+
+ auto StoreBuilder = MIRBuilder.buildInstr(opcode(GenericOpcode::G_STORE));
+ if (!StoreBuilder)
+ return std::unexpected(StoreBuilder.error());
+ if (auto Added = StoreBuilder->addUse(*VarArgsAddress); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = StoreBuilder->addUse(*Destination); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = StoreBuilder->addMemOperand(*Instruction.memOperands().front()); !Added)
+ return std::unexpected(Added.error());
+ if (auto Built = StoreBuilder->build(); !Built)
+ return std::unexpected(Built.error());
+
+ return Instruction.eraseFromParent();
+}
+
+Expected<void> RISCVLegalizerInfo::legalizeCustom(LegalizerHelper &Helper,
+ MachineInstr &Instruction,
+ const LegalizationDecision &Decision) const {
+ if (Decision.Action != LegalizationAction::Custom)
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "RISC-V custom legalizer received a non-custom action");
+
+ MachineFunction &Function = Helper.MIRBuilder.function();
+ const TargetDescription &Target = Helper.MIRBuilder.target();
+ if (Instruction.opcode() == opcode(GenericOpcode::G_BRJT))
+ return legalizeBRJT(Instruction, Helper.MIRBuilder);
+ if (Instruction.opcode() == opcode(GenericOpcode::G_VASTART))
+ return legalizeVAStart(Instruction, Helper.MIRBuilder);
+ if (Instruction.opcode() == opcode(GenericOpcode::G_ADD) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_SUB)) {
+ auto XLenType = LowLevelType::makeCategoryNeutralScalar(64);
+ if (!XLenType)
+ return std::unexpected(XLenType.error());
+
+ Helper.Observer.changingInstr(Instruction);
+ if (auto Widened = Helper.widenScalarSrc(Instruction, *XLenType, 1, GenericOpcode::G_ANYEXT);
+ !Widened)
+ return std::unexpected(Widened.error());
+ if (auto Widened = Helper.widenScalarSrc(Instruction, *XLenType, 2, GenericOpcode::G_ANYEXT);
+ !Widened)
+ return std::unexpected(Widened.error());
+
+ auto ALUDestination = Function.registerInfo().createVirtualRegister(*XLenType);
+ if (!ALUDestination)
+ return std::unexpected(ALUDestination.error());
+ auto SignExtendedDestination = Function.registerInfo().createVirtualRegister(*XLenType);
+ if (!SignExtendedDestination)
+ return std::unexpected(SignExtendedDestination.error());
+
+ MachineOperand *DestinationOperand = Instruction.operandAt(0);
+ const Register *Destination =
+ DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>();
+ if (Destination == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V word arithmetic requires a register destination");
+ const Register OriginalDestination = *Destination;
+ if (auto Point = Helper.MIRBuilder.setInsertPointAfter(Instruction); !Point)
+ return std::unexpected(Point.error());
+ if (auto Built =
+ Helper.MIRBuilder.buildSExtInReg(*SignExtendedDestination, *ALUDestination, 32);
+ !Built)
+ return std::unexpected(Built.error());
+ if (auto Built = Helper.MIRBuilder.buildTrunc(OriginalDestination, *SignExtendedDestination);
+ !Built)
+ return std::unexpected(Built.error());
+ if (auto Changed = DestinationOperand->setRegister(*ALUDestination); !Changed)
+ return std::unexpected(Changed.error());
+ Helper.Observer.changedInstr(Instruction);
+ return {};
+ }
+ if (Instruction.opcode() == opcode(GenericOpcode::G_SEXT_INREG)) {
+ const MachineOperand *DestinationOperand = Instruction.operandAt(0);
+ const MachineOperand *SizeOperand = Instruction.operandAt(2);
+ const Register *Destination =
+ DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>();
+ const std::int64_t *Size =
+ SizeOperand == nullptr ? nullptr : SizeOperand->getIf<std::int64_t>();
+ if (Destination == nullptr || Size == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_SEXT_INREG has invalid operands");
+ auto DestinationType = Function.registerInfo().getType(*Destination);
+ if (!DestinationType)
+ return std::unexpected(DestinationType.error());
+ if (DestinationType->minimumBitWidth() == 64 && *Size == 32)
+ return {};
+ if (Subtarget.features().has(RISCVExtension::Zbb) && (*Size == 8 || *Size == 16))
+ return {};
+ return Helper.lower(Instruction, LegalizationDecision{
+ .Action = LegalizationAction::Lower,
+ .TypeIndex = 0,
+ .NewType = {},
+ });
+ }
+ if (Instruction.opcode() == opcode(GenericOpcode::G_ASHR) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_LSHR) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_SHL)) {
+ auto XLenType = LowLevelType::makeCategoryNeutralScalar(64);
+ if (!XLenType)
+ return std::unexpected(XLenType.error());
+ const MachineOperand *ShiftOperand = Instruction.operandAt(2);
+ const Register *ShiftAmount =
+ ShiftOperand == nullptr ? nullptr : ShiftOperand->getIf<Register>();
+ if (ShiftAmount == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V shift legalization requires a register shift amount");
+
+ const GenericOpcode Generic = static_cast<GenericOpcode>(Instruction.opcode().value());
+ if (getIConstantVRegValWithLookThrough(*ShiftAmount, Function.registerInfo())) {
+ GenericOpcode ExtensionOpcode = GenericOpcode::G_ANYEXT;
+ if (Generic == GenericOpcode::G_ASHR)
+ ExtensionOpcode = GenericOpcode::G_SEXT;
+ else if (Generic == GenericOpcode::G_LSHR)
+ ExtensionOpcode = GenericOpcode::G_ZEXT;
+
+ Helper.Observer.changingInstr(Instruction);
+ if (auto Widened = Helper.widenScalarSrc(Instruction, *XLenType, 1, ExtensionOpcode);
+ !Widened)
+ return std::unexpected(Widened.error());
+ if (auto Widened = Helper.widenScalarSrc(Instruction, *XLenType, 2, GenericOpcode::G_ZEXT);
+ !Widened)
+ return std::unexpected(Widened.error());
+ if (auto Widened = Helper.widenScalarDst(Instruction, *XLenType); !Widened)
+ return std::unexpected(Widened.error());
+ Helper.Observer.changedInstr(Instruction);
+ return {};
+ }
+
+ Helper.Observer.changingInstr(Instruction);
+ if (auto Widened = Helper.widenScalarSrc(Instruction, *XLenType, 1, GenericOpcode::G_ANYEXT);
+ !Widened)
+ return std::unexpected(Widened.error());
+ if (auto Widened = Helper.widenScalarSrc(Instruction, *XLenType, 2, GenericOpcode::G_ANYEXT);
+ !Widened)
+ return std::unexpected(Widened.error());
+ if (auto Widened = Helper.widenScalarDst(Instruction, *XLenType); !Widened)
+ return std::unexpected(Widened.error());
+ auto WordOpcode = getRISCVWOpcode(Generic);
+ if (!WordOpcode)
+ return std::unexpected(WordOpcode.error());
+ if (auto Changed = Instruction.setOpcode(*WordOpcode); !Changed)
+ return std::unexpected(Changed.error());
+ Helper.Observer.changedInstr(Instruction);
+ return {};
+ }
+ if (Instruction.opcode() == opcode(GenericOpcode::G_SDIV) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_UDIV) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_UREM) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_ROTL) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_ROTR)) {
+ auto XLenType = LowLevelType::makeCategoryNeutralScalar(64);
+ if (!XLenType)
+ return std::unexpected(XLenType.error());
+
+ Helper.Observer.changingInstr(Instruction);
+ if (auto Widened = Helper.widenScalarSrc(Instruction, *XLenType, 1, GenericOpcode::G_ANYEXT);
+ !Widened)
+ return std::unexpected(Widened.error());
+ if (auto Widened = Helper.widenScalarSrc(Instruction, *XLenType, 2, GenericOpcode::G_ANYEXT);
+ !Widened)
+ return std::unexpected(Widened.error());
+ if (auto Widened = Helper.widenScalarDst(Instruction, *XLenType); !Widened)
+ return std::unexpected(Widened.error());
+ auto WordOpcode = getRISCVWOpcode(static_cast<GenericOpcode>(Instruction.opcode().value()));
+ if (!WordOpcode)
+ return std::unexpected(WordOpcode.error());
+ if (auto Changed = Instruction.setOpcode(*WordOpcode); !Changed)
+ return std::unexpected(Changed.error());
+ Helper.Observer.changedInstr(Instruction);
+ return {};
+ }
+ if (Instruction.opcode() == opcode(GenericOpcode::G_CTLZ) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_CTTZ) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_CTLS)) {
+ auto XLenType = LowLevelType::makeCategoryNeutralScalar(64);
+ if (!XLenType)
+ return std::unexpected(XLenType.error());
+
+ Helper.Observer.changingInstr(Instruction);
+ if (auto Widened = Helper.widenScalarSrc(Instruction, *XLenType, 1, GenericOpcode::G_ANYEXT);
+ !Widened) {
+ return std::unexpected(Widened.error());
+ }
+ if (auto Widened = Helper.widenScalarDst(Instruction, *XLenType); !Widened)
+ return std::unexpected(Widened.error());
+ auto WordOpcode = getRISCVWOpcode(static_cast<GenericOpcode>(Instruction.opcode().value()));
+ if (!WordOpcode)
+ return std::unexpected(WordOpcode.error());
+ if (auto Changed = Instruction.setOpcode(*WordOpcode); !Changed)
+ return std::unexpected(Changed.error());
+ Helper.Observer.changedInstr(Instruction);
+ return {};
+ }
+ if (Instruction.opcode() == opcode(GenericOpcode::G_FPTOSI) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_FPTOUI)) {
+ auto XLenType = LowLevelType::makeCategoryNeutralScalar(64);
+ if (!XLenType)
+ return std::unexpected(XLenType.error());
+
+ Helper.Observer.changingInstr(Instruction);
+ if (auto Widened = Helper.widenScalarDst(Instruction, *XLenType); !Widened)
+ return std::unexpected(Widened.error());
+ auto WordOpcode = getRISCVWOpcode(static_cast<GenericOpcode>(Instruction.opcode().value()));
+ if (!WordOpcode)
+ return std::unexpected(WordOpcode.error());
+ if (auto Changed = Instruction.setOpcode(*WordOpcode); !Changed)
+ return std::unexpected(Changed.error());
+ if (auto RoundingMode = Instruction.addOperand(MachineOperand::immediate(1)); !RoundingMode)
+ return std::unexpected(RoundingMode.error());
+ Helper.Observer.changedInstr(Instruction);
+ return {};
+ }
+ if (Instruction.opcode() == opcode(GenericOpcode::G_IS_FPCLASS)) {
+ const MachineOperand *DestinationOperand = Instruction.operandAt(0);
+ const MachineOperand *SourceOperand = Instruction.operandAt(1);
+ const MachineOperand *MaskOperand = Instruction.operandAt(2);
+ const Register *Destination =
+ DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>();
+ const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>();
+ if (Destination == nullptr || Source == nullptr || MaskOperand == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_IS_FPCLASS has invalid operands");
+ auto Mask = signedImmediate(*MaskOperand);
+ if (!Mask)
+ return std::unexpected(Mask.error());
+ if (*Mask < 0 || *Mask > 0x3ff)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V G_IS_FPCLASS class mask must fit ten bits");
+
+ auto XLenType = LowLevelType::makeCategoryNeutralScalar(64);
+ if (!XLenType)
+ return std::unexpected(XLenType.error());
+ const std::uint64_t ClassMask = static_cast<std::uint64_t>(*Mask);
+ const std::uint64_t RISCVClassMask = (ClassMask >> 2U) | ((ClassMask & 0x3U) << 8U);
+
+ auto FClass = Function.registerInfo().createVirtualRegister(*XLenType);
+ auto MaskConstant = Function.registerInfo().createVirtualRegister(*XLenType);
+ auto Zero = Function.registerInfo().createVirtualRegister(*XLenType);
+ auto And = Function.registerInfo().createVirtualRegister(*XLenType);
+ if (!FClass)
+ return std::unexpected(FClass.error());
+ if (!MaskConstant)
+ return std::unexpected(MaskConstant.error());
+ if (!Zero)
+ return std::unexpected(Zero.error());
+ if (!And)
+ return std::unexpected(And.error());
+ if (auto Point = Helper.MIRBuilder.setInsertPointBefore(Instruction); !Point)
+ return std::unexpected(Point.error());
+ if (auto Built = Helper.MIRBuilder.buildConstant(*MaskConstant,
+ static_cast<std::int64_t>(RISCVClassMask));
+ !Built) {
+ return std::unexpected(Built.error());
+ }
+ if (auto Built = Helper.MIRBuilder.buildConstant(*Zero, 0); !Built)
+ return std::unexpected(Built.error());
+ if (auto Built = Helper.MIRBuilder.buildInstr(instructionOpcode(InstructionOpcode::G_FCLASS),
+ {*FClass}, {*Source});
+ !Built) {
+ return std::unexpected(Built.error());
+ }
+ if (auto Built = Helper.MIRBuilder.buildAnd(*And, *FClass, *MaskConstant); !Built)
+ return std::unexpected(Built.error());
+ if (auto Built =
+ Helper.MIRBuilder.buildICmp(IntegerPredicate::NotEqual, *Destination, *And, *Zero);
+ !Built) {
+ return std::unexpected(Built.error());
+ }
+ return Instruction.eraseFromParent();
+ }
+ if (Instruction.opcode() == opcode(GenericOpcode::G_ANYEXT) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_SEXT) ||
+ Instruction.opcode() == opcode(GenericOpcode::G_ZEXT)) {
+ return foldConstantExtension(Instruction, Function,
+ static_cast<GenericOpcode>(Instruction.opcode().value()));
+ }
+ if (Instruction.opcode() == opcode(GenericOpcode::G_CONSTANT)) {
+ const MachineOperand *Immediate = Instruction.operandAt(1);
+ const std::optional<LowLevelType> Type = resultType(Function, Instruction);
+ if (Immediate != nullptr && Type && Type->isCategoryNeutralScalar() &&
+ Type->scalarBitWidth() == 64) {
+ auto Value = signedImmediate(*Immediate);
+ if (Value && shouldUseRV64IntegerConstantPool(*Value, Subtarget.features())) {
+ Helper.Observer.changingInstr(Instruction);
+ auto Lowered = lowerConstantToPool(Instruction, Function, Target);
+ if (!Lowered)
+ return std::unexpected(Lowered.error());
+ Helper.Observer.changedInstr(Instruction);
+ return {};
+ }
+ return {};
+ }
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RISC-V constant custom legalization is unavailable");
+ }
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RISC-V legalizer has no lowering rule for this instruction");
+}
+
+Expected<bool> RISCVLegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
+ MachineInstr &Instruction) const {
+ const MachineOperand *IntrinsicMachineOperand = Instruction.operandAt(0);
+ const auto *Intrinsic = IntrinsicMachineOperand == nullptr
+ ? nullptr
+ : IntrinsicMachineOperand->getIf<rcc::IntrinsicOperand>();
+ if (Intrinsic == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V intrinsic legalization requires an intrinsic operand");
+ if (Intrinsic->ID != intrinsicID(GenericIntrinsic::VACopy))
+ return false;
+ if (Instruction.operandCount() != 3)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "llvm.va_copy requires destination and source operands");
+
+ const MachineOperand *DestinationOperand = Instruction.operandAt(1);
+ const MachineOperand *SourceOperand = Instruction.operandAt(2);
+ const Register *Destination =
+ DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>();
+ const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>();
+ if (Destination == nullptr || Source == nullptr || !Destination->isVirtual() ||
+ !Source->isVirtual()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "llvm.va_copy requires virtual-register pointer operands");
+ }
+
+ MachineFunction &Function = Helper.MIRBuilder.function();
+ auto DestinationType = Function.registerInfo().getType(*Destination);
+ if (!DestinationType)
+ return std::unexpected(DestinationType.error());
+ auto SourceType = Function.registerInfo().getType(*Source);
+ if (!SourceType)
+ return std::unexpected(SourceType.error());
+ if (!DestinationType->isPointer() || !SourceType->isPointer() ||
+ *DestinationType != *SourceType || DestinationType->scalarBitWidth() == 0) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "llvm.va_copy operands must have the same scalar pointer type");
+ }
+
+ const auto AddressSpace = DestinationType->addressSpace();
+ if (!AddressSpace)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "llvm.va_copy pointer type has no address space");
+ auto Layout = DataLayout::parse(TargetDataLayout);
+ if (!Layout)
+ return std::unexpected(Layout.error());
+ const GenericConstantType PointerType = GenericConstantType::pointer(*AddressSpace);
+ auto PointerWidth = Layout->getPointerSizeInBits(*AddressSpace);
+ if (!PointerWidth)
+ return std::unexpected(PointerWidth.error());
+ if (*PointerWidth != DestinationType->scalarBitWidth())
+ return makeUnexpected(ErrorCode::VerificationError,
+ "llvm.va_copy pointer type disagrees with the target data layout");
+ auto ABIAlignment = Layout->getABITypeAlign(PointerType);
+ if (!ABIAlignment)
+ return std::unexpected(ABIAlignment.error());
+ auto Size = MemorySize::fixed((static_cast<std::uint64_t>(*PointerWidth) + 7U) / 8U);
+ auto Alignment = rcc::Alignment::create(*ABIAlignment);
+ if (!Size || !Alignment)
+ return makeUnexpected(ErrorCode::InternalError,
+ "llvm.va_copy pointer type has an invalid memory layout");
+
+ MachineMemOperandDesc LoadDescription;
+ LoadDescription.Accesses = {MemoryAccessFlag::Load};
+ LoadDescription.Size = *Size;
+ LoadDescription.MemoryType = *DestinationType;
+ LoadDescription.Align = *Alignment;
+ auto LoadMemory = Function.createMemOperand(std::move(LoadDescription));
+ if (!LoadMemory)
+ return std::unexpected(LoadMemory.error());
+
+ MachineMemOperandDesc StoreDescription;
+ StoreDescription.Accesses = {MemoryAccessFlag::Store};
+ StoreDescription.Size = *Size;
+ StoreDescription.MemoryType = *DestinationType;
+ StoreDescription.Align = *Alignment;
+ auto StoreMemory = Function.createMemOperand(std::move(StoreDescription));
+ if (!StoreMemory)
+ return std::unexpected(StoreMemory.error());
+
+ auto Temporary = Function.registerInfo().createVirtualRegister(*DestinationType);
+ if (!Temporary)
+ return std::unexpected(Temporary.error());
+
+ if (auto Point = Helper.MIRBuilder.setInsertPointBefore(Instruction); !Point)
+ return std::unexpected(Point.error());
+ auto LoadBuilder = Helper.MIRBuilder.buildInstr(opcode(GenericOpcode::G_LOAD));
+ if (!LoadBuilder)
+ return std::unexpected(LoadBuilder.error());
+ if (auto Added = LoadBuilder->addDef(*Temporary); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = LoadBuilder->addUse(*Source); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = LoadBuilder->addMemOperand(**LoadMemory); !Added)
+ return std::unexpected(Added.error());
+ if (auto Built = LoadBuilder->build(); !Built)
+ return std::unexpected(Built.error());
+
+ auto StoreBuilder = Helper.MIRBuilder.buildInstr(opcode(GenericOpcode::G_STORE));
+ if (!StoreBuilder)
+ return std::unexpected(StoreBuilder.error());
+ if (auto Added = StoreBuilder->addUse(*Temporary); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = StoreBuilder->addUse(*Destination); !Added)
+ return std::unexpected(Added.error());
+ if (auto Added = StoreBuilder->addMemOperand(**StoreMemory); !Added)
+ return std::unexpected(Added.error());
+ if (auto Built = StoreBuilder->build(); !Built)
+ return std::unexpected(Built.error());
+
+ if (auto Removed = Instruction.eraseFromParent(); !Removed)
+ return std::unexpected(Removed.error());
+ return true;
+}
+
+bool RISCVLegalizerInfo::isSExtCheaperThanZExt(LowLevelType SourceType,
+ LowLevelType DestinationType) const {
+ return SourceType.isCategoryNeutralScalar() && DestinationType.isCategoryNeutralScalar() &&
+ SourceType.scalarBitWidth() == 32 && DestinationType.scalarBitWidth() == 64;
+}
+
+} // namespace rcc::riscv