diff options
Diffstat (limited to 'backend/lib/Target/RISCV/RISCVCodeGen.cpp')
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVCodeGen.cpp | 6536 |
1 files changed, 0 insertions, 6536 deletions
diff --git a/backend/lib/Target/RISCV/RISCVCodeGen.cpp b/backend/lib/Target/RISCV/RISCVCodeGen.cpp deleted file mode 100644 index b610d81..0000000 --- a/backend/lib/Target/RISCV/RISCVCodeGen.cpp +++ /dev/null @@ -1,6536 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/Target/RISCV/RISCVCodeGen.h" - -#include "RISCVImmediateMaterialization.h" -#include "rcc/CodeGen/GenericOpcodes.h" -#include "rcc/CodeGen/LowLevelType.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineIRBuilder.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineMemOperand.h" -#include "rcc/CodeGen/MachineOperand.h" -#include "rcc/CodeGen/MachineRegisterInfo.h" -#include "rcc/CodeGen/TargetDescription.h" -#include "rcc/Target/RISCV/RISCVCallLowering.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]] Opcode targetOpcode(InstructionOpcode Value) noexcept { - return Opcode::fromValue(static_cast<std::uint32_t>(Value)); -} - -[[nodiscard]] std::optional<LLT> registerType(const MachineFunction &Function, - const MachineOperand &Operand) { - const Register *Reg = Operand.getIf<Register>(); - if (Reg == nullptr || !Reg->isVirtual()) - return std::nullopt; - auto Info = Function.registerInfo().virtualRegisterInfo(*Reg); - if (!Info) - return std::nullopt; - return Info->get().Type; -} - -[[nodiscard]] std::optional<LLT> resultType(const MachineFunction &Function, - const MachineInstr &Instruction) { - const MachineOperand *Result = Instruction.operandAt(0); - return Result == nullptr ? std::nullopt : registerType(Function, *Result); -} - -[[nodiscard]] std::optional<LLT> operandType(const MachineFunction &Function, - const MachineInstr &Instruction, std::size_t Index) { - const MachineOperand *Operand = Instruction.operandAt(Index); - return Operand == nullptr ? std::nullopt : registerType(Function, *Operand); -} - -[[nodiscard]] bool isRVFloatType(LLT Type) noexcept { - if (!Type.isFloat()) - return false; - const std::optional<FloatSemantics> Semantics = Type.floatSemantics(); - return Semantics && - (*Semantics == FloatSemantics::IEEESingle || *Semantics == FloatSemantics::IEEEDouble); -} - -[[nodiscard]] bool isSupportedIntegerType(LLT Type) noexcept { - return ((Type.isInteger() || Type.isAnyScalar()) && Type.scalarBitWidth() != 0 && - Type.scalarBitWidth() <= 64) || - (Type.isPointer() && Type.scalarBitWidth() == 64); -} - -[[nodiscard]] bool isSupportedIntegerScalar(LLT Type) noexcept { - return (Type.isInteger() || Type.isAnyScalar()) && Type.scalarBitWidth() != 0 && - Type.scalarBitWidth() <= 64; -} - -[[nodiscard]] bool isNativeIntegerScalar(LLT Type) noexcept { - return (Type.isInteger() || Type.isAnyScalar()) && - (Type.scalarBitWidth() == 32 || Type.scalarBitWidth() == 64); -} - -[[nodiscard]] bool isScalarGPRType(LLT Type) noexcept { - return ((Type.isInteger() || Type.isAnyScalar()) && Type.scalarBitWidth() != 0 && - Type.scalarBitWidth() <= 64) || - (Type.isPointer() && Type.scalarBitWidth() == 64); -} - -[[nodiscard]] bool isI1Type(LLT Type) noexcept { - return (Type.isInteger() || Type.isAnyScalar()) && Type.scalarBitWidth() == 1; -} - -[[nodiscard]] bool isScalarIntegerExtension(LLT Destination, LLT Source) noexcept { - return (Destination.isInteger() || Destination.isAnyScalar()) && - (Source.isInteger() || Source.isAnyScalar()) && Destination.scalarBitWidth() != 0 && - Source.scalarBitWidth() != 0 && Source.scalarBitWidth() < Destination.scalarBitWidth() && - Destination.scalarBitWidth() <= 64; -} - -[[nodiscard]] Expected<IntegerImmediate> -extendIntegerImmediate(const IntegerImmediate &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 IntegerImmediate::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 DestinationInfo = Function.registerInfo().virtualRegisterInfo(*Destination); - if (!DestinationInfo || !DestinationInfo->get().Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V constant extension destination has no type"); - const std::uint32_t DestinationWidth = DestinationInfo->get().Type->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()); - MachineBasicBlock *Parent = Instruction.parent(); - if (Parent == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V constant extension is detached during folding"); - return Parent->removeInstruction(Instruction); - } - - 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); - MachineBasicBlock *Parent = Def->parent(); - if (Parent == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V constant extension source definition is detached"); - auto Removed = Parent->removeInstruction(*Def); - 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 {}; -} - -struct WideConstantUnmergePlan final { - MachineInstr *Extension = nullptr; - MachineInstr *Unmerge = nullptr; - std::vector<Register> Outputs; - bool SignExtend = false; -}; - -[[nodiscard]] MachineInstr *singleRegisterUse(const MachineFunction &Function, Register Reg) { - MachineInstr *UseInstruction = nullptr; - auto &RegisterInfo = const_cast<MachineRegisterInfo &>(Function.registerInfo()); - for (MachineOperand &Operand : RegisterInfo.useOperands(Reg)) { - if (Operand.parent() == nullptr) - continue; - if (UseInstruction != nullptr) - return nullptr; - UseInstruction = Operand.parent(); - } - return UseInstruction; -} - -[[nodiscard]] std::optional<WideConstantUnmergePlan> -wideConstantUnmergePlan(const MachineInstr &Constant, const MachineFunction &Function) { - const MachineOperand *ResultOperand = Constant.operandAt(0); - const Register *Result = ResultOperand == nullptr ? nullptr : ResultOperand->getIf<Register>(); - if (Result == nullptr || !Result->isVirtual()) - return std::nullopt; - - MachineInstr *FirstUse = singleRegisterUse(Function, *Result); - if (FirstUse == nullptr) - return std::nullopt; - MachineInstr *Extension = nullptr; - MachineInstr *Unmerge = FirstUse; - if (FirstUse->opcode() == opcode(GenericOpcode::G_ANYEXT) || - FirstUse->opcode() == opcode(GenericOpcode::G_SEXT) || - FirstUse->opcode() == opcode(GenericOpcode::G_ZEXT)) { - Extension = FirstUse; - const MachineOperand *ExtendedOperand = Extension->operandAt(0); - const Register *Extended = - ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); - if (Extended == nullptr || !Extended->isVirtual()) - return std::nullopt; - Unmerge = singleRegisterUse(Function, *Extended); - if (Unmerge == nullptr) - return std::nullopt; - } - if (Unmerge->opcode() != opcode(GenericOpcode::G_UNMERGE_VALUES) || - Unmerge->operandCount() < 3) - return std::nullopt; - - WideConstantUnmergePlan Plan; - Plan.Extension = Extension; - Plan.Unmerge = Unmerge; - Plan.SignExtend = Extension != nullptr && Extension->opcode() != opcode(GenericOpcode::G_ZEXT); - for (std::size_t Index = 0; Index + 1U < Unmerge->operandCount(); ++Index) { - const MachineOperand *Operand = Unmerge->operandAt(Index); - const Register *Reg = Operand == nullptr ? nullptr : Operand->getIf<Register>(); - if (Reg == nullptr || !Reg->isVirtual() || !Operand->isDef()) - return std::nullopt; - const auto Info = Function.registerInfo().virtualRegisterInfo(*Reg); - if (!Info || !Info->get().Type || Info->get().Type->scalarBitWidth() == 0 || - Info->get().Type->scalarBitWidth() > 64 || - (!Info->get().Type->isInteger() && !Info->get().Type->isAnyScalar())) - return std::nullopt; - Plan.Outputs.push_back(*Reg); - } - const MachineOperand *SourceOperand = Unmerge->operandAt(Unmerge->operandCount() - 1U); - const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); - const Register *ExpectedSource = Result; - if (Extension != nullptr) { - const MachineOperand *ExtendedOperand = Extension->operandAt(0); - ExpectedSource = ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); - } - if (Source == nullptr || ExpectedSource == nullptr || *Source != *ExpectedSource) - return std::nullopt; - return Plan; -} - -[[nodiscard]] Expected<IntegerImmediate> -wideConstantWord(const IntegerImmediate &Value, std::size_t WordIndex, std::uint32_t Width, - bool SignExtend) { - std::uint64_t Raw = 0; - if (WordIndex < Value.words().size()) - Raw = Value.words()[WordIndex]; - else if (SignExtend && Value.isNegative()) - Raw = std::numeric_limits<std::uint64_t>::max(); - const std::size_t WordBitOffset = WordIndex * 64U; - if (SignExtend && Value.isNegative() && WordBitOffset < Value.bitWidth()) { - const std::uint32_t Remaining = - Value.bitWidth() - static_cast<std::uint32_t>(WordBitOffset); - if (Remaining < 64) - Raw |= ~std::uint64_t{0} << Remaining; - } - if (Width < 64) - Raw &= (std::uint64_t{1} << Width) - 1U; - return IntegerImmediate::create(Width, std::span<const std::uint64_t>(&Raw, 1)); -} - -[[nodiscard]] Expected<void> foldWideConstant(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target) { - const auto Plan = wideConstantUnmergePlan(Instruction, Function); - if (!Plan) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide constant requires a G_UNMERGE_VALUES use"); - const MachineOperand *ImmediateOperand = Instruction.operandAt(1); - const auto *Integer = ImmediateOperand == nullptr - ? nullptr - : ImmediateOperand->getIf<IntegerImmediateOperand>(); - if (Integer == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide constant has no integer immediate"); - - std::vector<IntegerImmediate> Words; - Words.reserve(Plan->Outputs.size()); - for (std::size_t Index = 0; Index < Plan->Outputs.size(); ++Index) { - const auto Info = Function.registerInfo().virtualRegisterInfo(Plan->Outputs[Index]); - if (!Info || !Info->get().Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide constant output has no virtual-register type"); - auto Word = wideConstantWord(Integer->Value, Index, Info->get().Type->scalarBitWidth(), - Plan->SignExtend); - if (!Word) - return std::unexpected(Word.error()); - Words.push_back(std::move(*Word)); - } - - std::vector<std::size_t> Representatives(Plan->Outputs.size()); - for (std::size_t Index = 0; Index < Representatives.size(); ++Index) { - Representatives[Index] = Index; - for (std::size_t Previous = 0; Previous < Index; ++Previous) { - if (Words[Previous] == Words[Index]) { - Representatives[Index] = Previous; - break; - } - } - } - - const Register FirstOutput = Plan->Outputs.front(); - auto Retargeted = Instruction.operandAt(0)->setRegister(FirstOutput); - if (!Retargeted) - return std::unexpected(Retargeted.error()); - 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(Words.front()))); - if (!Added) - return std::unexpected(Added.error()); - - MachineBasicBlock *Block = Instruction.parent(); - if (Block == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide constant is detached during legalization"); - if (Plan->Unmerge != &Instruction) { - auto RemovedUnmerge = Plan->Unmerge->parent()->removeInstruction(*Plan->Unmerge); - if (!RemovedUnmerge) - return std::unexpected(RemovedUnmerge.error()); - } - if (Plan->Extension != nullptr) { - auto RemovedExtension = Plan->Extension->parent()->removeInstruction(*Plan->Extension); - if (!RemovedExtension) - return std::unexpected(RemovedExtension.error()); - } - - for (std::size_t Index = 1; Index < Plan->Outputs.size(); ++Index) { - if (Representatives[Index] == Index) - continue; - auto Replaced = Function.registerInfo().replaceRegWith( - Plan->Outputs[Index], Plan->Outputs[Representatives[Index]]); - if (!Replaced) - return std::unexpected(Replaced.error()); - } - - MachineInstr *Last = &Instruction; - for (std::size_t Index = 1; Index < Plan->Outputs.size(); ++Index) { - if (Representatives[Index] != Index) - continue; - MachineIRBuilder Builder(Function, Target); - auto Point = Builder.setInsertPointAfter(*Last); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_CONSTANT)); - if (!Pending) - return std::unexpected(Pending.error()); - auto Def = Pending->addDef(Plan->Outputs[Index]); - if (!Def) - return std::unexpected(Def.error()); - auto Value = Pending->addIntegerImmediate(std::move(Words[Index])); - if (!Value) - return std::unexpected(Value.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - Last = *Built; - } - return {}; -} - -[[nodiscard]] Expected<Register> -buildIntegerConstantBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, - std::uint32_t Width, std::uint64_t Value); - -[[nodiscard]] Expected<Register> -buildBinaryBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, - GenericOpcode Generic, LLT Type, Register Left, - Register Right); - -[[nodiscard]] Expected<Register> buildUnaryBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, - GenericOpcode Generic, LLT Type, Register Source); - -[[nodiscard]] Expected<void> eraseInstruction(MachineInstr &Instruction); - -[[nodiscard]] Expected<void> rewriteUnary(MachineInstr &Instruction, GenericOpcode Generic, - Register Destination, Register Source); - -struct NarrowAnyExtBinaryPlan final { - MachineInstr *Binary = nullptr; - GenericOpcode Generic = GenericOpcode::Invalid; - std::array<Register, 2> WideInputs{}; - std::vector<MachineInstr *> Artifacts; - std::vector<MachineInstr *> Constants; - std::uint32_t SourceWidth = 0; -}; - -[[nodiscard]] std::optional<NarrowAnyExtBinaryPlan> -narrowAnyExtBinaryPlan(const MachineInstr &Extension, const MachineFunction &Function) { - const MachineOperand *DestinationOperand = Extension.operandAt(0); - const MachineOperand *SourceOperand = Extension.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 || !Destination->isVirtual() || - !Source->isVirtual() || singleRegisterUse(Function, *Source) != &Extension) - return std::nullopt; - const auto DestinationInfo = Function.registerInfo().virtualRegisterInfo(*Destination); - const auto SourceInfo = Function.registerInfo().virtualRegisterInfo(*Source); - if (!DestinationInfo || !SourceInfo || !DestinationInfo->get().Type || !SourceInfo->get().Type) - return std::nullopt; - const LLT DestinationType = *DestinationInfo->get().Type; - const LLT SourceType = *SourceInfo->get().Type; - if (!isScalarIntegerExtension(DestinationType, SourceType) || - DestinationType.scalarBitWidth() != 64) - return std::nullopt; - - const MachineOperand *SourceDefinition = Function.registerInfo().getUniqueVRegDef(*Source); - MachineInstr *Binary = - SourceDefinition == nullptr ? nullptr : const_cast<MachineInstr *>(SourceDefinition->parent()); - if (Binary == nullptr || Binary->operandCount() != 3) - return std::nullopt; - const GenericOpcode Generic = static_cast<GenericOpcode>(Binary->opcode().value()); - if (Generic != GenericOpcode::G_ADD && Generic != GenericOpcode::G_SUB && - Generic != GenericOpcode::G_MUL && Generic != GenericOpcode::G_AND && - Generic != GenericOpcode::G_OR && Generic != GenericOpcode::G_XOR && - Generic != GenericOpcode::G_SHL) - return std::nullopt; - - NarrowAnyExtBinaryPlan Plan; - Plan.Binary = Binary; - Plan.Generic = Generic; - Plan.SourceWidth = SourceType.scalarBitWidth(); - const auto AddArtifact = [&Plan](MachineInstr *Artifact) { - if (std::ranges::find(Plan.Artifacts, Artifact) == Plan.Artifacts.end()) - Plan.Artifacts.push_back(Artifact); - }; - const auto TruncatedWideSource = [&](MachineInstr *Truncation, - MachineInstr *OnlyUse) -> std::optional<Register> { - if (Truncation == nullptr || Truncation->opcode() != opcode(GenericOpcode::G_TRUNC) || - Truncation->operandCount() != 2) - return std::nullopt; - const MachineOperand *NarrowOperand = Truncation->operandAt(0); - const Register *Narrow = NarrowOperand == nullptr ? nullptr : NarrowOperand->getIf<Register>(); - const MachineOperand *WideOperand = Truncation->operandAt(1); - const Register *Wide = WideOperand == nullptr ? nullptr : WideOperand->getIf<Register>(); - const std::optional<LLT> WideType = - WideOperand == nullptr ? std::nullopt : registerType(Function, *WideOperand); - if (Narrow == nullptr || Wide == nullptr || !Wide->isVirtual() || !WideType || - *WideType != DestinationType || singleRegisterUse(Function, *Narrow) != OnlyUse) - return std::nullopt; - return *Wide; - }; - for (std::size_t Index = 0; Index != Plan.WideInputs.size(); ++Index) { - const MachineOperand *Operand = Binary->operandAt(Index + 1U); - const Register *Reg = Operand == nullptr ? nullptr : Operand->getIf<Register>(); - if (Reg == nullptr || !Reg->isVirtual()) - return std::nullopt; - const auto Info = Function.registerInfo().virtualRegisterInfo(*Reg); - if (!Info || !Info->get().Type) - return std::nullopt; - if (*Info->get().Type == DestinationType) { - Plan.WideInputs[Index] = *Reg; - continue; - } - if (*Info->get().Type != SourceType) - return std::nullopt; - - const MachineOperand *Definition = Function.registerInfo().getUniqueVRegDef(*Reg); - MachineInstr *Def = Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); - if (auto Wide = TruncatedWideSource(Def, Binary)) { - Plan.WideInputs[Index] = *Wide; - AddArtifact(Def); - continue; - } - if (Def != nullptr && - (Def->opcode() == opcode(GenericOpcode::COPY) || - Def->opcode() == opcode(GenericOpcode::G_ANYEXT)) && - Def->operandCount() == 2 && singleRegisterUse(Function, *Reg) == Binary) { - const MachineOperand *NarrowOperand = Def->operandAt(1); - const Register *Narrow = - NarrowOperand == nullptr ? nullptr : NarrowOperand->getIf<Register>(); - const MachineOperand *NarrowDefinition = - Narrow == nullptr ? nullptr : Function.registerInfo().getUniqueVRegDef(*Narrow); - MachineInstr *Truncation = NarrowDefinition == nullptr - ? nullptr - : const_cast<MachineInstr *>(NarrowDefinition->parent()); - if (auto Wide = TruncatedWideSource(Truncation, Def)) { - Plan.WideInputs[Index] = *Wide; - AddArtifact(Def); - AddArtifact(Truncation); - continue; - } - } - if (Def != nullptr && Def->opcode() == opcode(GenericOpcode::G_CONSTANT) && - singleRegisterUse(Function, *Reg) == Binary) { - Plan.WideInputs[Index] = *Reg; - Plan.Constants.push_back(Def); - continue; - } - return std::nullopt; - } - return Plan; -} - -[[nodiscard]] Expected<void> widenConstantToRV64(MachineInstr &Instruction, - MachineFunction &Function) { - 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 narrow constant legalization has invalid operands"); - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - auto SetType = Function.registerInfo().setType(*Destination, *Type); - if (!SetType) - return std::unexpected(SetType.error()); - auto Extended = extendIntegerImmediate(Immediate->Value, 64, true); - if (!Extended) - return std::unexpected(Extended.error()); - 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()); - return {}; -} - -[[nodiscard]] Expected<Register> widenAtomicCompareExchangeSource(MachineInstr &Instruction, - MachineFunction &Function, - MachineIRBuilder &Builder, - Register Source, LLT WideType) { - const auto SourceInfo = Function.registerInfo().virtualRegisterInfo(Source); - if (!SourceInfo || !SourceInfo->get().Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V compare-exchange source has no scalar type"); - if (SourceInfo->get().Type->scalarBitWidth() == WideType.scalarBitWidth()) - return Source; - - const MachineOperand *Definition = Function.registerInfo().getUniqueVRegDef(Source); - MachineInstr *Def = - Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); - if (Def != nullptr && Def->opcode() == opcode(GenericOpcode::G_CONSTANT) && - singleRegisterUse(Function, Source) == &Instruction) { - auto Widened = widenConstantToRV64(*Def, Function); - if (!Widened) - return std::unexpected(Widened.error()); - return Source; - } - return buildUnaryBefore(Builder, Instruction, GenericOpcode::G_ANYEXT, WideType, Source); -} - -[[nodiscard]] Expected<void> lowerAtomicCompareExchange(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const std::optional<LLT> OriginalType = resultType(Function, Instruction); - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *CompareOperand = Instruction.operandAt(2); - const MachineOperand *NewValueOperand = Instruction.operandAt(3); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Compare = CompareOperand == nullptr ? nullptr : CompareOperand->getIf<Register>(); - const Register *NewValue = - NewValueOperand == nullptr ? nullptr : NewValueOperand->getIf<Register>(); - if (!OriginalType || Destination == nullptr || Compare == nullptr || NewValue == nullptr || - OriginalType->scalarBitWidth() >= 64) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V narrow compare-exchange has invalid operands"); - const Register OriginalDestination = *Destination; - - auto WideType = LLT::makeAnyScalar(64); - if (!WideType) - return std::unexpected(WideType.error()); - MachineIRBuilder Builder(Function, Target); - auto WideCompare = - widenAtomicCompareExchangeSource(Instruction, Function, Builder, *Compare, *WideType); - if (!WideCompare) - return std::unexpected(WideCompare.error()); - auto WideNewValue = - widenAtomicCompareExchangeSource(Instruction, Function, Builder, *NewValue, *WideType); - if (!WideNewValue) - return std::unexpected(WideNewValue.error()); - - Register WideDestination; - MachineInstr *FoldedExtension = singleRegisterUse(Function, OriginalDestination); - if (FoldedExtension != nullptr && FoldedExtension->opcode() == opcode(GenericOpcode::G_ANYEXT) && - operandType(Function, *FoldedExtension, 0) - .transform([](LLT Type) { return Type.scalarBitWidth() == 64; }) - .value_or(false)) { - const MachineOperand *WideDestinationOperand = FoldedExtension->operandAt(0); - const Register *FoldedDestination = - WideDestinationOperand == nullptr ? nullptr : WideDestinationOperand->getIf<Register>(); - if (FoldedDestination == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V compare-exchange extension has no destination"); - WideDestination = *FoldedDestination; - } else { - auto Created = Function.registerInfo().createVirtualRegister(*WideType); - if (!Created) - return std::unexpected(Created.error()); - WideDestination = *Created; - FoldedExtension = nullptr; - } - - if (auto Replaced = Instruction.operandAt(0)->setRegister(WideDestination); !Replaced) - return std::unexpected(Replaced.error()); - if (auto Replaced = Instruction.operandAt(2)->setRegister(*WideCompare); !Replaced) - return std::unexpected(Replaced.error()); - if (auto Replaced = Instruction.operandAt(3)->setRegister(*WideNewValue); !Replaced) - return std::unexpected(Replaced.error()); - - if (FoldedExtension != nullptr) - return eraseInstruction(*FoldedExtension); - - auto Point = Builder.setInsertPointAfter(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto Truncation = Builder.buildInstr(opcode(GenericOpcode::G_TRUNC)); - if (!Truncation) - return std::unexpected(Truncation.error()); - if (auto Added = Truncation->addDef(OriginalDestination); !Added) - return std::unexpected(Added.error()); - if (auto Added = Truncation->addUse(WideDestination); !Added) - return std::unexpected(Added.error()); - auto Built = Truncation->build(); - if (!Built) - return std::unexpected(Built.error()); - return {}; -} - -[[nodiscard]] Expected<void> -lowerAtomicCompareExchangeWithSuccess(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target) { - const MachineOperand *OldValueOperand = Instruction.operandAt(0); - const MachineOperand *SuccessOperand = Instruction.operandAt(1); - const MachineOperand *AddressOperand = Instruction.operandAt(2); - const MachineOperand *CompareOperand = Instruction.operandAt(3); - const MachineOperand *NewValueOperand = Instruction.operandAt(4); - const Register *OldValue = - OldValueOperand == nullptr ? nullptr : OldValueOperand->getIf<Register>(); - const Register *Success = SuccessOperand == nullptr ? nullptr : SuccessOperand->getIf<Register>(); - const Register *Address = AddressOperand == nullptr ? nullptr : AddressOperand->getIf<Register>(); - const Register *Compare = CompareOperand == nullptr ? nullptr : CompareOperand->getIf<Register>(); - const Register *NewValue = - NewValueOperand == nullptr ? nullptr : NewValueOperand->getIf<Register>(); - const std::optional<LLT> ValueType = resultType(Function, Instruction); - if (OldValue == nullptr || Success == nullptr || Address == nullptr || Compare == nullptr || - NewValue == nullptr || !ValueType || Instruction.memOperands().size() != 1) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V compare-exchange-with-success has invalid operands"); - - auto AtomicResult = Function.registerInfo().createVirtualRegister(*ValueType); - if (!AtomicResult) - return std::unexpected(AtomicResult.error()); - MachineIRBuilder Builder(Function, Target); - auto Point = Builder.setInsertPointBefore(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto Atomic = Builder.buildInstr(opcode(GenericOpcode::G_ATOMIC_CMPXCHG)); - if (!Atomic) - return std::unexpected(Atomic.error()); - if (auto Added = Atomic->addDef(*AtomicResult); !Added) - return std::unexpected(Added.error()); - if (auto Added = Atomic->addUse(*Address); !Added) - return std::unexpected(Added.error()); - if (auto Added = Atomic->addUse(*Compare); !Added) - return std::unexpected(Added.error()); - if (auto Added = Atomic->addUse(*NewValue); !Added) - return std::unexpected(Added.error()); - if (auto Added = Atomic->addMemOperand(*Instruction.memOperands().front()); !Added) - return std::unexpected(Added.error()); - auto BuiltAtomic = Atomic->build(); - if (!BuiltAtomic) - return std::unexpected(BuiltAtomic.error()); - - Point = Builder.setInsertPointBefore(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto CompareResult = Builder.buildInstr(opcode(GenericOpcode::G_ICMP)); - if (!CompareResult) - return std::unexpected(CompareResult.error()); - if (auto Added = CompareResult->addDef(*Success); !Added) - return std::unexpected(Added.error()); - if (auto Added = CompareResult->addIntegerPredicate(IntegerPredicate::Equal); !Added) - return std::unexpected(Added.error()); - if (auto Added = CompareResult->addUse(*AtomicResult); !Added) - return std::unexpected(Added.error()); - if (auto Added = CompareResult->addUse(*Compare); !Added) - return std::unexpected(Added.error()); - auto BuiltCompare = CompareResult->build(); - if (!BuiltCompare) - return std::unexpected(BuiltCompare.error()); - - while (!Instruction.memOperands().empty()) { - auto Erased = Instruction.eraseMemOperand(0); - if (!Erased) - return std::unexpected(Erased.error()); - } - return rewriteUnary(Instruction, GenericOpcode::COPY, *OldValue, *AtomicResult); -} - -[[nodiscard]] Expected<Register> widenIntegerCompareSource(MachineInstr &Instruction, - MachineFunction &Function, - MachineIRBuilder &Builder, - Register Source, LLT WideType) { - const auto SourceInfo = Function.registerInfo().virtualRegisterInfo(Source); - if (!SourceInfo || !SourceInfo->get().Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V integer compare source has no scalar type"); - const LLT SourceType = *SourceInfo->get().Type; - if (SourceType.scalarBitWidth() == WideType.scalarBitWidth()) - return Source; - - const MachineOperand *Definition = Function.registerInfo().getUniqueVRegDef(Source); - MachineInstr *Def = - Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); - std::optional<Register> ExistingExtension; - bool AmbiguousExtension = false; - auto &RegisterInfo = Function.registerInfo(); - for (MachineOperand &Use : RegisterInfo.useOperands(Source)) { - MachineInstr *User = Use.parent(); - if (User == nullptr || User == &Instruction || User->parent() == nullptr || - (User->opcode() != opcode(GenericOpcode::G_ANYEXT) && - User->opcode() != opcode(GenericOpcode::G_SEXT))) { - continue; - } - const MachineOperand *ExtendedOperand = User->operandAt(0); - const Register *Extended = - ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); - const std::optional<LLT> ExtendedType = - ExtendedOperand == nullptr ? std::nullopt : registerType(Function, *ExtendedOperand); - if (Extended == nullptr || !ExtendedType || *ExtendedType != WideType || - (Def == nullptr || (Def->opcode() != opcode(GenericOpcode::G_CONSTANT) && - User->opcode() != opcode(GenericOpcode::G_SEXT)))) { - continue; - } - if (ExistingExtension) { - AmbiguousExtension = true; - break; - } - ExistingExtension = *Extended; - } - if (ExistingExtension && !AmbiguousExtension) - return *ExistingExtension; - if (Def != nullptr && Def->opcode() == opcode(GenericOpcode::G_CONSTANT) && - singleRegisterUse(Function, Source) == &Instruction) { - auto Widened = widenConstantToRV64(*Def, Function); - if (!Widened) - return std::unexpected(Widened.error()); - return Source; - } - - if (Def != nullptr && Def->opcode() == opcode(GenericOpcode::G_TRUNC) && - Def->operandCount() == 2) { - const MachineOperand *WideOperand = Def->operandAt(1); - const Register *WideSource = WideOperand == nullptr ? nullptr : WideOperand->getIf<Register>(); - const std::optional<LLT> ExistingWideType = - WideOperand == nullptr ? std::nullopt : registerType(Function, *WideOperand); - if (WideSource != nullptr && ExistingWideType && *ExistingWideType == WideType) { - auto Result = Function.registerInfo().createVirtualRegister(WideType); - if (!Result) - return std::unexpected(Result.error()); - auto Point = Builder.setInsertPointBefore(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_SEXT_INREG)); - if (!Pending) - return std::unexpected(Pending.error()); - if (auto Added = Pending->addDef(*Result); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addUse(*WideSource); !Added) - return std::unexpected(Added.error()); - if (auto Added = - Pending->addImmediate(static_cast<std::int64_t>(SourceType.scalarBitWidth())); - !Added) { - return std::unexpected(Added.error()); - } - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Result; - } - } - return buildUnaryBefore(Builder, Instruction, GenericOpcode::G_SEXT, WideType, Source); -} - -[[nodiscard]] Expected<void> lowerIntegerCompare(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *LeftOperand = Instruction.operandAt(2); - const MachineOperand *RightOperand = Instruction.operandAt(3); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - if (Destination == nullptr || Left == nullptr || Right == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V integer compare has invalid register operands"); - const Register OriginalDestination = *Destination; - const Register OriginalLeft = *Left; - const Register OriginalRight = *Right; - - auto WideType = LLT::makeAnyScalar(64); - if (!WideType) - return std::unexpected(WideType.error()); - MachineIRBuilder Builder(Function, Target); - auto WideLeft = - widenIntegerCompareSource(Instruction, Function, Builder, OriginalLeft, *WideType); - if (!WideLeft) - return std::unexpected(WideLeft.error()); - auto WideRight = - widenIntegerCompareSource(Instruction, Function, Builder, OriginalRight, *WideType); - if (!WideRight) - return std::unexpected(WideRight.error()); - if (auto Replaced = Instruction.operandAt(2)->setRegister(*WideLeft); !Replaced) - return std::unexpected(Replaced.error()); - if (auto Replaced = Instruction.operandAt(3)->setRegister(*WideRight); !Replaced) - return std::unexpected(Replaced.error()); - - const auto DestinationInfo = Function.registerInfo().virtualRegisterInfo(OriginalDestination); - if (!DestinationInfo || !DestinationInfo->get().Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V integer compare destination has no scalar type"); - if (DestinationInfo->get().Type->scalarBitWidth() == 64) - return {}; - - MachineInstr *FoldedExtension = singleRegisterUse(Function, OriginalDestination); - if (FoldedExtension != nullptr && - (FoldedExtension->opcode() == opcode(GenericOpcode::G_ANYEXT) || - FoldedExtension->opcode() == opcode(GenericOpcode::G_SEXT) || - FoldedExtension->opcode() == opcode(GenericOpcode::G_ZEXT)) && - operandType(Function, *FoldedExtension, 0) - .transform([](LLT Type) { return Type.scalarBitWidth() == 64; }) - .value_or(false)) { - const MachineOperand *ExtendedOperand = FoldedExtension->operandAt(0); - const Register *Extended = - ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); - if (Extended == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V integer compare extension has no destination"); - const Register WideDestination = *Extended; - if (auto Replaced = Instruction.operandAt(0)->setRegister(WideDestination); !Replaced) - return std::unexpected(Replaced.error()); - return eraseInstruction(*FoldedExtension); - } - - auto WideDestination = Function.registerInfo().createVirtualRegister(*WideType); - if (!WideDestination) - return std::unexpected(WideDestination.error()); - if (auto Replaced = Instruction.operandAt(0)->setRegister(*WideDestination); !Replaced) - return std::unexpected(Replaced.error()); - if (Function.registerInfo().useEmpty(OriginalDestination)) - return {}; - auto Point = Builder.setInsertPointAfter(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto Truncation = Builder.buildInstr(opcode(GenericOpcode::G_TRUNC)); - if (!Truncation) - return std::unexpected(Truncation.error()); - if (auto Added = Truncation->addDef(OriginalDestination); !Added) - return std::unexpected(Added.error()); - if (auto Added = Truncation->addUse(*WideDestination); !Added) - return std::unexpected(Added.error()); - auto Built = Truncation->build(); - if (!Built) - return std::unexpected(Built.error()); - return {}; -} - -[[nodiscard]] Expected<void> foldNarrowAnyExtBinary(MachineInstr &Extension, - MachineFunction &Function, - const TargetDescription &Target) { - const auto Plan = narrowAnyExtBinaryPlan(Extension, Function); - if (!Plan) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V narrow any-extension does not have a legal binary chain"); - for (MachineInstr *Constant : Plan->Constants) { - auto Widened = widenConstantToRV64(*Constant, Function); - if (!Widened) - return std::unexpected(Widened.error()); - } - for (std::size_t Index = 0; Index != Plan->WideInputs.size(); ++Index) { - auto Replaced = Plan->Binary->operandAt(Index + 1U)->setRegister(Plan->WideInputs[Index]); - if (!Replaced) - return std::unexpected(Replaced.error()); - } - for (MachineInstr *Artifact : Plan->Artifacts) { - if (Artifact->parent() == nullptr) - continue; - auto Removed = Artifact->parent()->removeInstruction(*Artifact); - if (!Removed) - return std::unexpected(Removed.error()); - } - - if (Plan->Generic == GenericOpcode::G_SHL) { - Register Amount = Plan->WideInputs[1]; - if (Plan->SourceWidth < 32 || Plan->SourceWidth > 32) { - const std::uint64_t Mask = - Plan->SourceWidth == 64 - ? std::numeric_limits<std::uint64_t>::max() - : (std::uint64_t{1} << Plan->SourceWidth) - 1U; - auto WideType = LLT::makeAnyScalar(64); - if (!WideType) - return std::unexpected(WideType.error()); - MachineIRBuilder Builder(Function, Target); - auto MaskValue = - buildIntegerConstantBefore(Builder, Extension, 64, Mask); - if (!MaskValue) - return std::unexpected(MaskValue.error()); - auto MaskedAmount = buildBinaryBefore(Builder, Extension, - GenericOpcode::G_AND, *WideType, - Amount, *MaskValue); - if (!MaskedAmount) - return std::unexpected(MaskedAmount.error()); - Amount = *MaskedAmount; - } - auto SetOpcode = Extension.setOpcode( - Plan->SourceWidth == 32 ? targetOpcode(InstructionOpcode::G_SLLW) - : opcode(GenericOpcode::G_SHL)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - while (Extension.operandCount() > 1) { - auto Erased = Extension.eraseOperand(1); - if (!Erased) - return std::unexpected(Erased.error()); - } - for (Register Input : {Plan->WideInputs[0], Amount}) { - auto Operand = MachineOperand::registerOperand( - Input, RegisterOperandFlags{RegisterOperandFlag::Use}); - if (!Operand) - return std::unexpected(Operand.error()); - auto Added = Extension.addOperand(std::move(*Operand)); - if (!Added) - return std::unexpected(Added.error()); - } - MachineBasicBlock *BinaryBlock = Plan->Binary->parent(); - if (BinaryBlock == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V narrow shift is detached during legalization"); - return BinaryBlock->removeInstruction(*Plan->Binary); - } - - if (Plan->SourceWidth == 32 && - (Plan->Generic == GenericOpcode::G_ADD || Plan->Generic == GenericOpcode::G_SUB)) { - auto WideType = LLT::makeAnyScalar(64); - if (!WideType) - return std::unexpected(WideType.error()); - auto Temporary = Function.registerInfo().createVirtualRegister(*WideType); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto Retargeted = Plan->Binary->operandAt(0)->setRegister(*Temporary); - if (!Retargeted) - return std::unexpected(Retargeted.error()); - auto SetOpcode = Extension.setOpcode(opcode(GenericOpcode::G_SEXT_INREG)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - auto ReplacedSource = Extension.operandAt(1)->setRegister(*Temporary); - if (!ReplacedSource) - return std::unexpected(ReplacedSource.error()); - auto Width = Extension.addOperand(MachineOperand::immediate(32)); - if (!Width) - return std::unexpected(Width.error()); - return {}; - } - - auto SetOpcode = Extension.setOpcode(opcode(Plan->Generic)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - while (Extension.operandCount() > 1) { - auto Erased = Extension.eraseOperand(1); - if (!Erased) - return std::unexpected(Erased.error()); - } - for (Register Input : Plan->WideInputs) { - auto Operand = MachineOperand::registerOperand( - Input, RegisterOperandFlags{RegisterOperandFlag::Use}); - if (!Operand) - return std::unexpected(Operand.error()); - auto Added = Extension.addOperand(std::move(*Operand)); - if (!Added) - return std::unexpected(Added.error()); - } - MachineBasicBlock *BinaryBlock = Plan->Binary->parent(); - if (BinaryBlock == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V narrow binary instruction is detached during legalization"); - auto Removed = BinaryBlock->removeInstruction(*Plan->Binary); - if (!Removed) - return std::unexpected(Removed.error()); - (void)Target; - return {}; -} - -[[nodiscard]] bool fitsSigned(std::int64_t Value, std::uint32_t Bits) noexcept { - const std::int64_t Minimum = -(std::int64_t{1} << (Bits - 1U)); - const std::int64_t Maximum = (std::int64_t{1} << (Bits - 1U)) - 1; - return Value >= Minimum && Value <= Maximum; -} - -[[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 DestinationInfo = Function.registerInfo().virtualRegisterInfo(*Destination); - if (!DestinationInfo || !DestinationInfo->get().Type || - DestinationInfo->get().Type->scalarBitWidth() != 64) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V constant-pool legalization requires a 64-bit destination"); - const LLT DestinationType = *DestinationInfo->get().Type; - 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 = LLT::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]] bool isSupportedNonAtomicMemoryAccess(const MachineInstr &Instruction) noexcept { - if (Instruction.memOperands().size() != 1) - return false; - const MachineMemOperand &Memory = *Instruction.memOperands().front(); - const MachineMemOperandDesc &Description = Memory.description(); - return !Memory.isAtomic() && Description.AddressSpace == 0 && Description.Size.isKnown() && - !Description.Size.isScalable() && Description.Size.minimumBytes() != 0; -} - -[[nodiscard]] bool isSupportedAtomicMemoryAccess(const MachineInstr &Instruction, - AtomicOperation Operation, - std::uint64_t Bytes) noexcept { - if (Instruction.memOperands().size() != 1 || (Bytes != 4 && Bytes != 8)) - return false; - const MachineMemOperand &Memory = *Instruction.memOperands().front(); - const MachineMemOperandDesc &Description = Memory.description(); - return Description.AtomicKind == Operation && Description.AddressSpace == 0 && - Description.Size.isKnown() && !Description.Size.isScalable() && - Description.Size.minimumBytes() == Bytes && Description.Align.bytes() >= Bytes && - Description.SyncScope.value() == 0; -} - -[[nodiscard]] bool isSupportedAtomicContainerAccess(const MachineInstr &Instruction, - AtomicOperation Operation, - std::uint64_t ContainerBytes) noexcept { - if (Instruction.memOperands().size() != 1 || ContainerBytes == 0) - return false; - const MachineMemOperandDesc &Description = Instruction.memOperands().front()->description(); - if (Description.AtomicKind != Operation || Description.AddressSpace != 0 || - !Description.Size.isKnown() || Description.Size.isScalable() || - Description.SyncScope.value() != 0) - return false; - const std::uint64_t MemoryBytes = Description.Size.minimumBytes(); - return (MemoryBytes == 1 || MemoryBytes == 2 || MemoryBytes == 4 || MemoryBytes == 8) && - MemoryBytes <= ContainerBytes && Description.Align.bytes() >= MemoryBytes; -} - -[[nodiscard]] std::optional<std::uint64_t> -memoryAccessBytes(const MachineInstr &Instruction) noexcept { - if (Instruction.memOperands().size() != 1) - return std::nullopt; - const MachineMemOperandDesc &Description = Instruction.memOperands().front()->description(); - if (!Description.Size.isKnown() || Description.Size.isScalable()) - return std::nullopt; - return Description.Size.minimumBytes(); -} - -[[nodiscard]] bool memoryMatchesType(const MachineInstr &Instruction, LLT Type) noexcept { - const auto Bytes = memoryAccessBytes(Instruction); - if (!Bytes || (!isSupportedIntegerType(Type) && !isRVFloatType(Type))) - return false; - if (isRVFloatType(Type)) - return Type.scalarBitWidth() == 32 ? *Bytes == 4 : *Bytes == 8; - const std::uint64_t ValueBytes = (Type.scalarBitWidth() + 7U) / 8U; - return (*Bytes == 1 || *Bytes == 2 || *Bytes == 4 || *Bytes == 8) && *Bytes <= ValueBytes; -} - -[[nodiscard]] Expected<RegisterClassID> requiredRegisterClass(const MachineFunction &Function, - Register Reg, - const TargetDescription &Target) { - if (!Reg.isVirtual()) - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V register-class selection requires a virtual register"); - auto Info = Function.registerInfo().virtualRegisterInfo(Reg); - if (!Info) - return std::unexpected(Info.error()); - if (!Info->get().Type) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V selection requires a type for every virtual register"); - } - const VirtualRegisterInfo &RegisterInfo = Info->get(); - const LLT Type = RegisterInfo.Type.value_or(LLT{}); - RegisterClassID RequiredClass; - if (isScalarGPRType(Type)) { - RequiredClass = RegisterClassID::fromValue(static_cast<std::uint32_t>(RegisterClass::GPR)); - } else if (Type.isFloat() && Type.scalarBitWidth() == 32) { - RequiredClass = RegisterClassID::fromValue(static_cast<std::uint32_t>(RegisterClass::FPR32)); - } else if (Type.isFloat() && Type.scalarBitWidth() == 64) { - RequiredClass = RegisterClassID::fromValue(static_cast<std::uint32_t>(RegisterClass::FPR64)); - } else { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V RV64GC has no register class for type '" + Type.toString() + "'"); - } - - if (Target.registers().registerClass(RequiredClass) == nullptr) { - return makeUnexpected(ErrorCode::InternalError, - "RISC-V target description is missing a required register class"); - } - if (RegisterInfo.Bank && !Target.registers().bankCoversClass(*RegisterInfo.Bank, RequiredClass)) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V virtual register bank cannot cover its required class"); - } - if (RegisterInfo.RegClass && - !Target.registers().isSubclass(*RegisterInfo.RegClass, RequiredClass)) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V virtual register has a conflicting register class"); - } - return RequiredClass; -} - -[[nodiscard]] Expected<void> appendImmediate(MachineInstr &Instruction, std::int64_t Value) { - return Instruction.addOperand(MachineOperand::immediate(Value)); -} - -[[nodiscard]] Expected<void> insertPhysicalRegister(MachineInstr &Instruction, std::size_t Index, - PhysicalRegister Reg, - RegisterOperandFlag Flag) { - auto Operand = MachineOperand::registerOperand(Register::fromPhysical(Reg), {Flag}); - if (!Operand) - return std::unexpected(Operand.error()); - return Instruction.insertOperand(Index, std::move(*Operand)); -} - -[[nodiscard]] Expected<void> insertVirtualUse(MachineInstr &Instruction, std::size_t Index, - Register Reg) { - auto Operand = MachineOperand::registerOperand(Reg, {RegisterOperandFlag::Use}); - if (!Operand) - return std::unexpected(Operand.error()); - return Instruction.insertOperand(Index, std::move(*Operand)); -} - -[[nodiscard]] Expected<Register> createTemporary(MachineFunction &Function, LLT Type, - const TargetDescription &Target) { - auto Temporary = Function.registerInfo().createVirtualRegister(Type); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto RequiredClass = requiredRegisterClass(Function, *Temporary, Target); - if (!RequiredClass) - return std::unexpected(RequiredClass.error()); - const RegisterBank BankKind = Type.isFloat() ? RegisterBank::FPR : RegisterBank::GPR; - auto Bank = Function.registerInfo().setRegisterBank( - *Temporary, RegisterBankID::fromValue(static_cast<std::uint32_t>(BankKind))); - if (!Bank) - return std::unexpected(Bank.error()); - auto Class = Function.registerInfo().setRegisterClass(*Temporary, *RequiredClass); - if (!Class) - return std::unexpected(Class.error()); - return *Temporary; -} - -[[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); -} - -[[nodiscard]] Expected<void> replaceRegisterUses(MachineFunction &Function, Register OldReg, - Register NewReg) { - std::vector<MachineOperand *> Uses; - for (MachineOperand &Operand : Function.registerInfo().useOperands(OldReg)) - Uses.push_back(&Operand); - for (MachineOperand *Operand : Uses) { - auto Replaced = Operand->setRegister(NewReg); - if (!Replaced) - return std::unexpected(Replaced.error()); - } - return {}; -} - -[[nodiscard]] Expected<void> eraseInstruction(MachineInstr &Instruction) { - MachineBasicBlock *Block = Instruction.parent(); - if (Block == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V legalizer cannot erase a detached instruction"); - return Block->removeInstruction(Instruction); -} - -[[nodiscard]] bool allDefsUnused(const MachineInstr &Instruction, - const MachineFunction &Function) { - for (const MachineOperand &Operand : Instruction.operands()) { - const Register *Reg = Operand.getIf<Register>(); - if (Reg != nullptr && Operand.isDef() && Reg->isVirtual() && - !Function.registerInfo().useEmpty(*Reg)) - return false; - } - return true; -} - -void eraseDeadArtifacts(std::span<MachineInstr *const> Candidates, - const MachineFunction &Function) { - bool Changed = false; - do { - Changed = false; - for (auto It = Candidates.rbegin(); It != Candidates.rend(); ++It) { - MachineInstr *Instruction = *It; - if (Instruction == nullptr || Instruction->parent() == nullptr || - !allDefsUnused(*Instruction, Function)) - continue; - const Opcode Value = Instruction->opcode(); - if (Value != opcode(GenericOpcode::G_TRUNC) && - Value != opcode(GenericOpcode::G_ANYEXT) && - Value != opcode(GenericOpcode::G_SEXT) && Value != opcode(GenericOpcode::G_ZEXT) && - Value != opcode(GenericOpcode::G_MERGE_VALUES) && - Value != opcode(GenericOpcode::G_UNMERGE_VALUES) && - Value != opcode(GenericOpcode::COPY)) - continue; - if (eraseInstruction(*Instruction)) - Changed = true; - } - } while (Changed); -} - -[[nodiscard]] Expected<Register> buildIntegerConstantBefore(MachineIRBuilder &Builder, - MachineInstr &Anchor, - std::uint32_t Width, - std::uint64_t Value) { - auto Type = LLT::makeAnyScalar(Width); - if (!Type) - return std::unexpected(Type.error()); - auto Result = Builder.function().registerInfo().createVirtualRegister(*Type); - if (!Result) - return std::unexpected(Result.error()); - auto Immediate = IntegerImmediate::fromUnsigned(Width, Value); - if (!Immediate) - return std::unexpected(Immediate.error()); - auto Point = Builder.setInsertPointBefore(Anchor); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_CONSTANT)); - if (!Pending) - return std::unexpected(Pending.error()); - if (auto Added = Pending->addDef(*Result); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addIntegerImmediate(std::move(*Immediate)); !Added) - return std::unexpected(Added.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Result; -} - -[[nodiscard]] Expected<Register> buildUnaryBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, - GenericOpcode Generic, LLT Type, - Register Source) { - auto Result = Builder.function().registerInfo().createVirtualRegister(Type); - if (!Result) - return std::unexpected(Result.error()); - auto Point = Builder.setInsertPointBefore(Anchor); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(opcode(Generic)); - if (!Pending) - return std::unexpected(Pending.error()); - if (auto Added = Pending->addDef(*Result); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addUse(Source); !Added) - return std::unexpected(Added.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Result; -} - -[[nodiscard]] Expected<Register> buildBinaryBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, - GenericOpcode Generic, LLT Type, Register Left, - Register Right) { - auto Result = Builder.function().registerInfo().createVirtualRegister(Type); - if (!Result) - return std::unexpected(Result.error()); - auto Point = Builder.setInsertPointBefore(Anchor); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(opcode(Generic)); - if (!Pending) - return std::unexpected(Pending.error()); - if (auto Added = Pending->addDef(*Result); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addUse(Left); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addUse(Right); !Added) - return std::unexpected(Added.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Result; -} - -[[nodiscard]] Expected<void> rewriteUnary(MachineInstr &Instruction, GenericOpcode Generic, - Register Destination, Register Source) { - auto SetOpcode = Instruction.setOpcode(opcode(Generic)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - while (Instruction.operandCount() != 0) { - auto Erased = Instruction.eraseOperand(Instruction.operandCount() - 1U); - if (!Erased) - return std::unexpected(Erased.error()); - } - auto Def = MachineOperand::registerOperand( - Destination, RegisterOperandFlags{RegisterOperandFlag::Define}); - if (!Def) - return std::unexpected(Def.error()); - if (auto Added = Instruction.addOperand(std::move(*Def)); !Added) - return std::unexpected(Added.error()); - auto Use = - MachineOperand::registerOperand(Source, RegisterOperandFlags{RegisterOperandFlag::Use}); - if (!Use) - return std::unexpected(Use.error()); - return Instruction.addOperand(std::move(*Use)); -} - -[[nodiscard]] Expected<void> widenNarrowMultiply(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const std::optional<LLT> Type = resultType(Function, Instruction); - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *LeftOperand = Instruction.operandAt(1); - const MachineOperand *RightOperand = Instruction.operandAt(2); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - if (!Type || Destination == nullptr || Left == nullptr || Right == nullptr || - Type->scalarBitWidth() >= 64) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V narrow multiply has invalid operands"); - } - - MachineInstr *Extension = singleRegisterUse(Function, *Destination); - if (Extension != nullptr && Extension->opcode() == opcode(GenericOpcode::G_ANYEXT)) { - const auto Plan = narrowAnyExtBinaryPlan(*Extension, Function); - if (Plan && Plan->Binary == &Instruction) - return foldNarrowAnyExtBinary(*Extension, Function, Target); - } - - auto WideType = LLT::makeAnyScalar(64); - if (!WideType) - return std::unexpected(WideType.error()); - MachineIRBuilder Builder(Function, Target); - auto WideLeft = buildUnaryBefore(Builder, Instruction, GenericOpcode::G_ANYEXT, *WideType, *Left); - if (!WideLeft) - return std::unexpected(WideLeft.error()); - auto WideRight = - buildUnaryBefore(Builder, Instruction, GenericOpcode::G_ANYEXT, *WideType, *Right); - if (!WideRight) - return std::unexpected(WideRight.error()); - auto Product = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_MUL, *WideType, *WideLeft, - *WideRight); - if (!Product) - return std::unexpected(Product.error()); - return rewriteUnary(Instruction, GenericOpcode::G_TRUNC, *Destination, *Product); -} - -[[nodiscard]] Expected<void> lowerMultiplyLibcall(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - CallingConvention Convention) { - const std::optional<LLT> Type = resultType(Function, Instruction); - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *LeftOperand = Instruction.operandAt(1); - const MachineOperand *RightOperand = Instruction.operandAt(2); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - if (!Type || Destination == nullptr || Left == nullptr || Right == nullptr || - Type->scalarBitWidth() != 64) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V multiply libcall requires three s64 registers"); - } - auto ABIAlignment = Alignment::create(8); - auto Callee = MachineCallTarget::externalSymbol("__muldi3"); - if (!ABIAlignment || !Callee) - return makeUnexpected(ErrorCode::InternalError, - "RISC-V multiply libcall ABI facts are unavailable"); - const auto Value = [Type = *Type, Alignment = *ABIAlignment](Register Reg, std::uint32_t Number) { - return ABIValue{ - .Value = Reg, - .Type = Type, - .OriginalValue = Number, - .FragmentIndex = 0, - .FragmentCount = 1, - .FragmentOffsetBytes = 0, - .OriginalAlignment = Alignment, - .Extension = ABIExtensionKind::None, - .Flags = {}, - .ByValSizeBytes = 0, - .ByValAlignment = Alignment, - }; - }; - - CallLoweringRequest Request; - Request.Convention = callingConventionID(Convention); - Request.Callee = std::move(*Callee); - Request.Arguments = {Value(*Left, 0), Value(*Right, 1)}; - Request.Results = {Value(*Destination, 0)}; - Request.FixedArgumentCount = Request.Arguments.size(); - - MachineIRBuilder Builder(Function, Target); - auto Point = Builder.setInsertPointBefore(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto Lowered = callLowering().lowerCall(Builder, Request); - if (!Lowered) - return std::unexpected(Lowered.error()); - return eraseInstruction(Instruction); -} - -[[nodiscard]] Expected<void> rewriteBinary(MachineInstr &Instruction, GenericOpcode Generic, - Register Destination, Register Left, Register Right) { - auto Rewritten = rewriteUnary(Instruction, Generic, Destination, Left); - if (!Rewritten) - return std::unexpected(Rewritten.error()); - auto Use = MachineOperand::registerOperand(Right, RegisterOperandFlags{RegisterOperandFlag::Use}); - if (!Use) - return std::unexpected(Use.error()); - return Instruction.addOperand(std::move(*Use)); -} - -[[nodiscard]] bool isKnownZeroExtended(const MachineFunction &Function, Register Reg, - std::uint32_t ActiveBits, unsigned Depth = 0) { - if (Depth > 8 || !Reg.isVirtual()) - return false; - const auto Info = Function.registerInfo().virtualRegisterInfo(Reg); - if (!Info || !Info->get().Type) - return false; - const std::uint32_t Width = Info->get().Type->scalarBitWidth(); - if (Width <= ActiveBits) - return true; - const MachineOperand *Definition = Function.registerInfo().getUniqueVRegDef(Reg); - const MachineInstr *Def = Definition == nullptr ? nullptr : Definition->parent(); - if (Def == nullptr) - return false; - if (Def->opcode() == opcode(GenericOpcode::G_ASSERT_ZEXT)) { - const MachineOperand *BitsOperand = Def->operandAt(2); - auto Bits = BitsOperand == nullptr - ? Expected<std::int64_t>(std::unexpected( - Error(ErrorCode::InvalidArgument, "missing asserted extension width"))) - : signedImmediate(*BitsOperand); - return Bits && *Bits >= 0 && static_cast<std::uint64_t>(*Bits) <= ActiveBits; - } - if (Def->opcode() == opcode(GenericOpcode::G_ZEXT)) { - const MachineOperand *SourceOperand = Def->operandAt(1); - const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); - if (Source == nullptr) - return false; - const auto SourceInfo = Function.registerInfo().virtualRegisterInfo(*Source); - return SourceInfo && SourceInfo->get().Type && - SourceInfo->get().Type->scalarBitWidth() <= ActiveBits; - } - if (Def->opcode() == opcode(GenericOpcode::G_TRUNC)) { - const MachineOperand *SourceOperand = Def->operandAt(1); - const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); - return Source != nullptr && isKnownZeroExtended(Function, *Source, ActiveBits, Depth + 1U); - } - return false; -} - -[[nodiscard]] Expected<Register> -zeroExtendToRV64(MachineIRBuilder &Builder, MachineInstr &Anchor, MachineFunction &Function, - Register Source, std::vector<MachineInstr *> &DeadCandidates) { - const auto Info = Function.registerInfo().virtualRegisterInfo(Source); - if (!Info || !Info->get().Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V merge source has no scalar type"); - const LLT SourceType = *Info->get().Type; - if (SourceType.scalarBitWidth() == 64) - return Source; - const MachineOperand *Definition = Function.registerInfo().getUniqueVRegDef(Source); - MachineInstr *Def = Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); - if (Def != nullptr && Def->opcode() == opcode(GenericOpcode::G_TRUNC)) { - const MachineOperand *WideOperand = Def->operandAt(1); - const Register *Wide = WideOperand == nullptr ? nullptr : WideOperand->getIf<Register>(); - if (Wide != nullptr) { - const auto WideInfo = Function.registerInfo().virtualRegisterInfo(*Wide); - if (WideInfo && WideInfo->get().Type && WideInfo->get().Type->scalarBitWidth() == 64 && - isKnownZeroExtended(Function, *Wide, SourceType.scalarBitWidth())) { - DeadCandidates.push_back(Def); - return *Wide; - } - } - } - auto WideType = LLT::makeAnyScalar(64); - if (!WideType) - return std::unexpected(WideType.error()); - return buildUnaryBefore(Builder, Anchor, GenericOpcode::G_ZEXT, *WideType, Source); -} - -[[nodiscard]] MachineInstr *singleUseInstruction(const MachineFunction &Function, Register Reg) { - MachineInstr *Result = nullptr; - auto &RegisterInfo = const_cast<MachineRegisterInfo &>(Function.registerInfo()); - for (MachineOperand &Operand : RegisterInfo.useOperands(Reg)) { - MachineInstr *Instruction = Operand.parent(); - if (Instruction == nullptr || Instruction->parent() == nullptr) - continue; - if (Result != nullptr && Result != Instruction) - return nullptr; - Result = Instruction; - } - return Result; -} - -[[nodiscard]] Expected<void> lowerWideMergeValues(MachineInstr &Instruction, - MachineFunction &Function) { - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const MachineOperand *LowOperand = Instruction.operandAt(1); - const Register *Low = LowOperand == nullptr ? nullptr : LowOperand->getIf<Register>(); - if (Destination == nullptr || Low == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide merge has invalid register operands"); - - std::vector<MachineInstr *> Truncations; - auto &RegisterInfo = Function.registerInfo(); - std::vector<MachineOperand *> Uses; - for (MachineOperand &Use : RegisterInfo.useOperands(*Destination)) - Uses.push_back(&Use); - for (MachineOperand *Use : Uses) { - MachineInstr *Trunc = Use->parent(); - if (Trunc == nullptr || Trunc->parent() == nullptr || - Trunc->opcode() != opcode(GenericOpcode::G_TRUNC) || Trunc->operandAt(1) != Use) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide merge is only representable through low-part truncation"); - const MachineOperand *TruncResultOperand = Trunc->operandAt(0); - const Register *TruncResult = - TruncResultOperand == nullptr ? nullptr : TruncResultOperand->getIf<Register>(); - const auto LowInfo = RegisterInfo.virtualRegisterInfo(*Low); - const auto ResultInfo = TruncResult == nullptr - ? Expected<std::reference_wrapper<const VirtualRegisterInfo>>( - std::unexpected(Error(ErrorCode::InvalidArgument, - "missing truncation result"))) - : RegisterInfo.virtualRegisterInfo(*TruncResult); - if (!LowInfo || !LowInfo->get().Type || !ResultInfo || !ResultInfo->get().Type || - ResultInfo->get().Type->scalarBitWidth() > LowInfo->get().Type->scalarBitWidth()) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide merge truncation spans more than its low source"); - if (*ResultInfo->get().Type == *LowInfo->get().Type) { - auto Replaced = replaceRegisterUses(Function, *TruncResult, *Low); - if (!Replaced) - return std::unexpected(Replaced.error()); - Truncations.push_back(Trunc); - } else { - auto Replaced = Use->setRegister(*Low); - if (!Replaced) - return std::unexpected(Replaced.error()); - } - } - for (MachineInstr *Trunc : Truncations) { - auto Erased = eraseInstruction(*Trunc); - if (!Erased) - return std::unexpected(Erased.error()); - } - return eraseInstruction(Instruction); -} - -[[nodiscard]] Expected<void> lowerMergeValues(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const std::optional<LLT> DestinationType = resultType(Function, Instruction); - if (!DestinationType || DestinationType->scalarBitWidth() == 0) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V merge destination has no scalar type"); - if (DestinationType->scalarBitWidth() > 64) - return lowerWideMergeValues(Instruction, Function); - - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - if (Destination == nullptr || Instruction.operandCount() < 3) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V merge has invalid register operands"); - - Register Result = *Destination; - MachineInstr *FoldedExtension = nullptr; - if (DestinationType->scalarBitWidth() < 64) { - MachineInstr *Use = singleUseInstruction(Function, *Destination); - if (Use != nullptr && Use->opcode() == opcode(GenericOpcode::G_ZEXT) && - operandType(Function, *Use, 0).transform([](LLT Type) { - return Type.scalarBitWidth() == 64; - }).value_or(false)) { - const MachineOperand *ExtendedOperand = Use->operandAt(0); - const Register *Extended = - ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); - if (Extended != nullptr) { - Result = *Extended; - FoldedExtension = Use; - } - } - } - - auto WideType = LLT::makeAnyScalar(64); - if (!WideType) - return std::unexpected(WideType.error()); - MachineIRBuilder Builder(Function, Target); - std::vector<MachineInstr *> DeadCandidates; - std::vector<Register> Terms; - Terms.reserve(Instruction.operandCount() - 1U); - for (std::size_t Index = 1; Index < Instruction.operandCount(); ++Index) { - const MachineOperand *Operand = Instruction.operandAt(Index); - const Register *Source = Operand == nullptr ? nullptr : Operand->getIf<Register>(); - if (Source == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V merge source is not a register"); - auto WideSource = zeroExtendToRV64(Builder, Instruction, Function, *Source, DeadCandidates); - if (!WideSource) - return std::unexpected(WideSource.error()); - if (Index == 1) { - Terms.push_back(*WideSource); - continue; - } - const std::optional<LLT> SourceType = registerType(Function, *Operand); - if (!SourceType) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V merge source has no scalar type"); - const std::uint64_t Shift = - static_cast<std::uint64_t>(SourceType->scalarBitWidth()) * (Index - 1U); - auto Amount = buildIntegerConstantBefore(Builder, Instruction, 64, Shift); - if (!Amount) - return std::unexpected(Amount.error()); - auto Shifted = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_SHL, *WideType, - *WideSource, *Amount); - if (!Shifted) - return std::unexpected(Shifted.error()); - Terms.push_back(*Shifted); - } - - Register Accumulator = Terms.front(); - for (std::size_t Index = 1; Index + 1U < Terms.size(); ++Index) { - auto Combined = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_OR, *WideType, - Accumulator, Terms[Index]); - if (!Combined) - return std::unexpected(Combined.error()); - Accumulator = *Combined; - } - if (DestinationType->scalarBitWidth() == 64 || FoldedExtension != nullptr) { - auto Rewritten = rewriteBinary(Instruction, GenericOpcode::G_OR, Result, Accumulator, - Terms.back()); - if (!Rewritten) - return std::unexpected(Rewritten.error()); - } else { - auto Combined = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_OR, *WideType, - Accumulator, Terms.back()); - if (!Combined) - return std::unexpected(Combined.error()); - auto Rewritten = rewriteUnary(Instruction, GenericOpcode::G_TRUNC, *Destination, *Combined); - if (!Rewritten) - return std::unexpected(Rewritten.error()); - } - if (FoldedExtension != nullptr) { - auto Erased = eraseInstruction(*FoldedExtension); - if (!Erased) - return std::unexpected(Erased.error()); - } - eraseDeadArtifacts(DeadCandidates, Function); - return {}; -} - -[[nodiscard]] Expected<void> -foldWideExtendedUnmerge(MachineInstr &Instruction, MachineFunction &Function) { - const std::size_t OutputCount = Instruction.operandCount() - 1U; - const MachineOperand *SourceOperand = Instruction.operandAt(OutputCount); - const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); - const MachineOperand *Definition = - Source == nullptr ? nullptr : Function.registerInfo().getUniqueVRegDef(*Source); - MachineInstr *Extension = - Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); - if (Extension == nullptr || - (Extension->opcode() != opcode(GenericOpcode::G_ZEXT) && - Extension->opcode() != opcode(GenericOpcode::G_ANYEXT) && - Extension->opcode() != opcode(GenericOpcode::G_SEXT))) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide unmerge requires an extension-defined source"); - const MachineOperand *NarrowOperand = Extension->operandAt(1); - const Register *Narrow = NarrowOperand == nullptr ? nullptr : NarrowOperand->getIf<Register>(); - if (Narrow == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide unmerge extension has no register source"); - const auto NarrowInfo = Function.registerInfo().virtualRegisterInfo(*Narrow); - if (!NarrowInfo || !NarrowInfo->get().Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide unmerge extension source has no type"); - const std::uint32_t NarrowWidth = NarrowInfo->get().Type->scalarBitWidth(); - - std::vector<MachineInstr *> DeadUsers; - for (std::size_t Index = 0; Index < OutputCount; ++Index) { - const MachineOperand *OutputOperand = Instruction.operandAt(Index); - const Register *Output = OutputOperand == nullptr ? nullptr : OutputOperand->getIf<Register>(); - if (Output == nullptr || Function.registerInfo().useEmpty(*Output)) - continue; - const auto OutputInfo = Function.registerInfo().virtualRegisterInfo(*Output); - if (!OutputInfo || !OutputInfo->get().Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide unmerge output has no type"); - const std::uint64_t Offset = - static_cast<std::uint64_t>(Index) * OutputInfo->get().Type->scalarBitWidth(); - if (Offset != 0) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide unmerge cannot materialize a used high wide part"); - if (OutputInfo->get().Type->scalarBitWidth() == NarrowWidth) { - auto Replaced = replaceRegisterUses(Function, *Output, *Narrow); - if (!Replaced) - return std::unexpected(Replaced.error()); - continue; - } - if (OutputInfo->get().Type->scalarBitWidth() < NarrowWidth) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide unmerge output unexpectedly narrows its source"); - std::vector<MachineOperand *> Uses; - for (MachineOperand &Use : Function.registerInfo().useOperands(*Output)) - Uses.push_back(&Use); - for (MachineOperand *Use : Uses) { - MachineInstr *Trunc = Use->parent(); - if (Trunc == nullptr || Trunc->parent() == nullptr || - Trunc->opcode() != opcode(GenericOpcode::G_TRUNC) || Trunc->operandAt(1) != Use) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide unmerge result is only representable through truncation"); - const MachineOperand *ResultOperand = Trunc->operandAt(0); - const Register *Result = ResultOperand == nullptr ? nullptr : ResultOperand->getIf<Register>(); - const auto ResultInfo = Result == nullptr - ? Expected<std::reference_wrapper<const VirtualRegisterInfo>>( - std::unexpected(Error(ErrorCode::InvalidArgument, - "missing truncation result"))) - : Function.registerInfo().virtualRegisterInfo(*Result); - if (!ResultInfo || !ResultInfo->get().Type || - ResultInfo->get().Type->scalarBitWidth() > NarrowWidth) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide unmerge truncation exceeds its source width"); - if (ResultInfo->get().Type->scalarBitWidth() == NarrowWidth) { - auto Replaced = replaceRegisterUses(Function, *Result, *Narrow); - if (!Replaced) - return std::unexpected(Replaced.error()); - DeadUsers.push_back(Trunc); - } else { - auto Replaced = Use->setRegister(*Narrow); - if (!Replaced) - return std::unexpected(Replaced.error()); - } - } - } - for (MachineInstr *User : DeadUsers) { - auto Erased = eraseInstruction(*User); - if (!Erased) - return std::unexpected(Erased.error()); - } - auto Erased = eraseInstruction(Instruction); - if (!Erased) - return std::unexpected(Erased.error()); - if (Function.registerInfo().useEmpty(*Source)) - return eraseInstruction(*Extension); - return {}; -} - -[[nodiscard]] Expected<void> lowerUnmergeValues(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - if (Instruction.operandCount() < 3) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V unmerge has invalid register operands"); - const std::size_t OutputCount = Instruction.operandCount() - 1U; - const std::optional<LLT> OutputType = operandType(Function, Instruction, 0); - const std::optional<LLT> SourceType = operandType(Function, Instruction, OutputCount); - if (!OutputType || !SourceType) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V unmerge operands have no scalar types"); - if (SourceType->scalarBitWidth() > 64 || OutputType->scalarBitWidth() > 64) - return foldWideExtendedUnmerge(Instruction, Function); - - const MachineOperand *SourceOperand = Instruction.operandAt(OutputCount); - const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); - if (Source == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V unmerge source is not a register"); - auto WideType = LLT::makeAnyScalar(64); - if (!WideType) - return std::unexpected(WideType.error()); - MachineIRBuilder Builder(Function, Target); - std::vector<MachineInstr *> FoldedExtensions; - for (std::size_t Index = 0; Index < OutputCount; ++Index) { - const MachineOperand *OutputOperand = Instruction.operandAt(Index); - const Register *Output = OutputOperand == nullptr ? nullptr : OutputOperand->getIf<Register>(); - if (Output == nullptr || Function.registerInfo().useEmpty(*Output)) - continue; - Register Extracted = *Source; - const std::uint64_t Offset = - static_cast<std::uint64_t>(Index) * OutputType->scalarBitWidth(); - if (Offset != 0) { - auto Amount = buildIntegerConstantBefore(Builder, Instruction, 64, Offset); - if (!Amount) - return std::unexpected(Amount.error()); - auto Shifted = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_LSHR, *WideType, - *Source, *Amount); - if (!Shifted) - return std::unexpected(Shifted.error()); - Extracted = *Shifted; - } - - MachineInstr *Extension = singleUseInstruction(Function, *Output); - const bool FoldZExt = Extension != nullptr && - Extension->opcode() == opcode(GenericOpcode::G_ZEXT) && - operandType(Function, *Extension, 0) - .transform([](LLT Type) { return Type.scalarBitWidth() == 64; }) - .value_or(false); - if (FoldZExt) { - const MachineOperand *ExtendedOperand = Extension->operandAt(0); - const Register *Extended = - ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); - if (Extended == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V unmerge extension has no destination"); - const std::uint64_t Mask = OutputType->scalarBitWidth() == 64 - ? std::numeric_limits<std::uint64_t>::max() - : (std::uint64_t{1} << OutputType->scalarBitWidth()) - 1U; - auto MaskReg = buildIntegerConstantBefore(Builder, Instruction, 64, Mask); - if (!MaskReg) - return std::unexpected(MaskReg.error()); - auto Masked = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_AND, *WideType, - Extracted, *MaskReg); - if (!Masked) - return std::unexpected(Masked.error()); - auto Replaced = replaceRegisterUses(Function, *Extended, *Masked); - if (!Replaced) - return std::unexpected(Replaced.error()); - FoldedExtensions.push_back(Extension); - continue; - } - auto Truncated = buildUnaryBefore(Builder, Instruction, GenericOpcode::G_TRUNC, *OutputType, - Extracted); - if (!Truncated) - return std::unexpected(Truncated.error()); - auto Replaced = replaceRegisterUses(Function, *Output, *Truncated); - if (!Replaced) - return std::unexpected(Replaced.error()); - } - for (MachineInstr *Extension : FoldedExtensions) { - auto Erased = eraseInstruction(*Extension); - if (!Erased) - return std::unexpected(Erased.error()); - } - return eraseInstruction(Instruction); -} - -struct WideValueParts final { - std::uint32_t BitWidth = 0; - std::vector<Register> Parts; - std::vector<MachineInstr *> Artifacts; -}; - -[[nodiscard]] Expected<WideValueParts> collectWideValueParts(Register Value, - MachineFunction &Function, - unsigned Depth = 0) { - if (Depth > 16) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide-value artifact chain is too deep"); - const auto Info = Function.registerInfo().virtualRegisterInfo(Value); - if (!Info || !Info->get().Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide value has no scalar type"); - const LLT Type = *Info->get().Type; - if ((!Type.isInteger() && !Type.isAnyScalar()) || Type.scalarBitWidth() == 0) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide-value decomposition requires an integer scalar"); - if (Type.scalarBitWidth() <= 64) - return WideValueParts{ - .BitWidth = Type.scalarBitWidth(), .Parts = {Value}, .Artifacts = {}}; - - const MachineOperand *Definition = Function.registerInfo().getUniqueVRegDef(Value); - MachineInstr *Def = - Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); - if (Def == nullptr) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide value has no decomposable definition"); - if (Def->opcode() == opcode(GenericOpcode::G_MERGE_VALUES)) { - WideValueParts Result; - Result.BitWidth = Type.scalarBitWidth(); - for (std::size_t Index = 1; Index < Def->operandCount(); ++Index) { - const MachineOperand *Operand = Def->operandAt(Index); - const Register *Part = Operand == nullptr ? nullptr : Operand->getIf<Register>(); - const std::optional<LLT> PartType = - Operand == nullptr ? std::nullopt : registerType(Function, *Operand); - if (Part == nullptr || !PartType || PartType->scalarBitWidth() != 64) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide merge must already use 64-bit limbs"); - Result.Parts.push_back(*Part); - } - Result.Artifacts.push_back(Def); - return Result; - } - if (Def->opcode() == opcode(GenericOpcode::G_TRUNC)) { - const MachineOperand *SourceOperand = Def->operandAt(1); - const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); - if (Source == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide truncation has no source register"); - auto Result = collectWideValueParts(*Source, Function, Depth + 1U); - if (!Result) - return std::unexpected(Result.error()); - Result->BitWidth = Type.scalarBitWidth(); - const std::size_t RequiredParts = (Type.scalarBitWidth() + 63U) / 64U; - if (Result->Parts.size() < RequiredParts) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide truncation source has too few limbs"); - Result->Parts.resize(RequiredParts); - Result->Artifacts.push_back(Def); - return Result; - } - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide value is not defined by merge/trunc artifacts"); -} - -struct WideResultPlan final { - std::vector<Register> Outputs; - MachineInstr *Extension = nullptr; - MachineInstr *Unmerge = nullptr; -}; - -[[nodiscard]] Expected<WideResultPlan> wideResultPlan(Register Result, - MachineFunction &Function, - std::size_t RequiredParts) { - MachineInstr *Use = singleUseInstruction(Function, Result); - WideResultPlan Plan; - if (Use != nullptr && - (Use->opcode() == opcode(GenericOpcode::G_ANYEXT) || - Use->opcode() == opcode(GenericOpcode::G_SEXT) || - Use->opcode() == opcode(GenericOpcode::G_ZEXT))) { - Plan.Extension = Use; - const MachineOperand *ExtendedOperand = Use->operandAt(0); - const Register *Extended = - ExtendedOperand == nullptr ? nullptr : ExtendedOperand->getIf<Register>(); - Use = Extended == nullptr ? nullptr : singleUseInstruction(Function, *Extended); - } - if (Use == nullptr || Use->opcode() != opcode(GenericOpcode::G_UNMERGE_VALUES)) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide result must feed an unmerge artifact"); - Plan.Unmerge = Use; - const std::size_t OutputCount = Use->operandCount() - 1U; - if (OutputCount < RequiredParts) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide result unmerge has too few outputs"); - Plan.Outputs.reserve(RequiredParts); - for (std::size_t Index = 0; Index < RequiredParts; ++Index) { - const MachineOperand *Operand = Use->operandAt(Index); - const Register *Output = Operand == nullptr ? nullptr : Operand->getIf<Register>(); - const std::optional<LLT> Type = - Operand == nullptr ? std::nullopt : registerType(Function, *Operand); - if (Output == nullptr || !Type || Type->scalarBitWidth() != 64) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V wide result must unmerge to 64-bit limbs"); - Plan.Outputs.push_back(*Output); - } - return Plan; -} - -[[nodiscard]] Expected<Register> -buildIntegerCompareBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, - IntegerPredicate Predicate, Register Left, Register Right) { - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - auto Result = Builder.function().registerInfo().createVirtualRegister(*Type); - if (!Result) - return std::unexpected(Result.error()); - auto Point = Builder.setInsertPointBefore(Anchor); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_ICMP)); - if (!Pending) - return std::unexpected(Pending.error()); - if (auto Added = Pending->addDef(*Result); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addIntegerPredicate(Predicate); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addUse(Left); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addUse(Right); !Added) - return std::unexpected(Added.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Result; -} - -[[nodiscard]] Expected<Register> -buildSelectBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, LLT Type, - Register Condition, Register TrueValue, Register FalseValue) { - auto Result = Builder.function().registerInfo().createVirtualRegister(Type); - if (!Result) - return std::unexpected(Result.error()); - auto Point = Builder.setInsertPointBefore(Anchor); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_SELECT)); - if (!Pending) - return std::unexpected(Pending.error()); - if (auto Added = Pending->addDef(*Result); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addUse(Condition); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addUse(TrueValue); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addUse(FalseValue); !Added) - return std::unexpected(Added.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Result; -} - -struct TwoLimbShiftResult final { - Register Low; - Register High; -}; - -[[nodiscard]] Expected<TwoLimbShiftResult> -emitTwoLimbLeftShiftBody(MachineIRBuilder &Builder, MachineInstr &Anchor, - Register Low, Register High, Register Amount, - Register AmountExcess, Register AmountLack, - Register Zero, Register IsShort, Register IsZero) { - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - auto LowShort = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, Low, Amount); - if (!LowShort) - return std::unexpected(LowShort.error()); - auto CrossLow = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_LSHR, *Type, Low, AmountLack); - if (!CrossLow) - return std::unexpected(CrossLow.error()); - auto HighShort = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, High, Amount); - if (!HighShort) - return std::unexpected(HighShort.error()); - auto ShortHigh = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_OR, *Type, - *CrossLow, *HighShort); - if (!ShortHigh) - return std::unexpected(ShortHigh.error()); - auto LongHigh = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, Low, AmountExcess); - if (!LongHigh) - return std::unexpected(LongHigh.error()); - auto ResultLow = - buildSelectBefore(Builder, Anchor, *Type, IsShort, *LowShort, Zero); - if (!ResultLow) - return std::unexpected(ResultLow.error()); - auto ShiftedHigh = - buildSelectBefore(Builder, Anchor, *Type, IsShort, *ShortHigh, *LongHigh); - if (!ShiftedHigh) - return std::unexpected(ShiftedHigh.error()); - auto ResultHigh = - buildSelectBefore(Builder, Anchor, *Type, IsZero, High, *ShiftedHigh); - if (!ResultHigh) - return std::unexpected(ResultHigh.error()); - return TwoLimbShiftResult{.Low = *ResultLow, .High = *ResultHigh}; -} - -[[nodiscard]] Expected<TwoLimbShiftResult> -emitTwoLimbLeftShift(MachineIRBuilder &Builder, MachineInstr &Anchor, - Register Low, Register High, Register Amount) { - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - auto Boundary = buildIntegerConstantBefore(Builder, Anchor, 64, 64); - if (!Boundary) - return std::unexpected(Boundary.error()); - auto AmountExcess = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, - Amount, *Boundary); - if (!AmountExcess) - return std::unexpected(AmountExcess.error()); - auto AmountLack = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, - *Boundary, Amount); - if (!AmountLack) - return std::unexpected(AmountLack.error()); - auto Zero = buildIntegerConstantBefore(Builder, Anchor, 64, 0); - if (!Zero) - return std::unexpected(Zero.error()); - auto IsShort = buildIntegerCompareBefore(Builder, Anchor, - IntegerPredicate::UnsignedLessThan, - Amount, *Boundary); - if (!IsShort) - return std::unexpected(IsShort.error()); - auto IsZero = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, - Amount, *Zero); - if (!IsZero) - return std::unexpected(IsZero.error()); - return emitTwoLimbLeftShiftBody(Builder, Anchor, Low, High, Amount, - *AmountExcess, *AmountLack, *Zero, *IsShort, - *IsZero); -} - -[[nodiscard]] Expected<Register> -emitLowLimbOfTwoLimbLogicalRightShift( - MachineIRBuilder &Builder, MachineInstr &Anchor, Register Low, Register High, - Register Amount, Register Boundary, Register Zero) { - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - auto AmountExcess = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, - Amount, Boundary); - if (!AmountExcess) - return std::unexpected(AmountExcess.error()); - auto AmountLack = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, - Boundary, Amount); - if (!AmountLack) - return std::unexpected(AmountLack.error()); - auto IsShort = buildIntegerCompareBefore(Builder, Anchor, - IntegerPredicate::UnsignedLessThan, - Amount, Boundary); - if (!IsShort) - return std::unexpected(IsShort.error()); - auto IsZero = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, - Amount, Zero); - if (!IsZero) - return std::unexpected(IsZero.error()); - auto LowShort = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_LSHR, *Type, Low, Amount); - if (!LowShort) - return std::unexpected(LowShort.error()); - auto HighShort = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, High, *AmountLack); - if (!HighShort) - return std::unexpected(HighShort.error()); - auto ShortValue = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_OR, *Type, - *LowShort, *HighShort); - if (!ShortValue) - return std::unexpected(ShortValue.error()); - auto LongValue = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_LSHR, *Type, - High, *AmountExcess); - if (!LongValue) - return std::unexpected(LongValue.error()); - auto Shifted = buildSelectBefore(Builder, Anchor, *Type, *IsShort, *ShortValue, - *LongValue); - if (!Shifted) - return std::unexpected(Shifted.error()); - return buildSelectBefore(Builder, Anchor, *Type, *IsZero, Low, *Shifted); -} - -[[nodiscard]] Expected<std::vector<Register>> -emitThreeLimbLeftShift(MachineIRBuilder &Builder, MachineInstr &Anchor, - std::span<const Register> Parts, Register Amount) { - if (Parts.size() != 3) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V three-limb shift requires exactly three limbs"); - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - - auto OuterBoundary = buildIntegerConstantBefore(Builder, Anchor, 64, 128); - if (!OuterBoundary) - return std::unexpected(OuterBoundary.error()); - auto OuterExcess = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, - Amount, *OuterBoundary); - if (!OuterExcess) - return std::unexpected(OuterExcess.error()); - auto OuterLack = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, - *OuterBoundary, Amount); - if (!OuterLack) - return std::unexpected(OuterLack.error()); - auto Zero = buildIntegerConstantBefore(Builder, Anchor, 64, 0); - if (!Zero) - return std::unexpected(Zero.error()); - auto OuterIsShort = buildIntegerCompareBefore( - Builder, Anchor, IntegerPredicate::UnsignedLessThan, Amount, *OuterBoundary); - if (!OuterIsShort) - return std::unexpected(OuterIsShort.error()); - auto OuterIsZero = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, - Amount, *Zero); - if (!OuterIsZero) - return std::unexpected(OuterIsZero.error()); - - auto InnerBoundary = buildIntegerConstantBefore(Builder, Anchor, 64, 64); - if (!InnerBoundary) - return std::unexpected(InnerBoundary.error()); - auto InnerExcess = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, - Amount, *InnerBoundary); - if (!InnerExcess) - return std::unexpected(InnerExcess.error()); - auto InnerLack = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, - *InnerBoundary, Amount); - if (!InnerLack) - return std::unexpected(InnerLack.error()); - auto InnerIsShort = buildIntegerCompareBefore( - Builder, Anchor, IntegerPredicate::UnsignedLessThan, Amount, *InnerBoundary); - if (!InnerIsShort) - return std::unexpected(InnerIsShort.error()); - auto InnerIsZero = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, - Amount, *Zero); - if (!InnerIsZero) - return std::unexpected(InnerIsZero.error()); - auto Lower = emitTwoLimbLeftShiftBody( - Builder, Anchor, Parts[0], Parts[1], Amount, *InnerExcess, *InnerLack, - *Zero, *InnerIsShort, *InnerIsZero); - if (!Lower) - return std::unexpected(Lower.error()); - - auto Cross = emitLowLimbOfTwoLimbLogicalRightShift( - Builder, Anchor, Parts[0], Parts[1], *OuterLack, *InnerBoundary, *Zero); - if (!Cross) - return std::unexpected(Cross.error()); - auto HighIsShort = buildIntegerCompareBefore( - Builder, Anchor, IntegerPredicate::UnsignedLessThan, Amount, *InnerBoundary); - if (!HighIsShort) - return std::unexpected(HighIsShort.error()); - auto HighShort = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, - Parts[2], Amount); - if (!HighShort) - return std::unexpected(HighShort.error()); - auto BoundedHigh = buildSelectBefore(Builder, Anchor, *Type, *HighIsShort, - *HighShort, *Zero); - if (!BoundedHigh) - return std::unexpected(BoundedHigh.error()); - auto ShortHigh = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_OR, *Type, - *Cross, *BoundedHigh); - if (!ShortHigh) - return std::unexpected(ShortHigh.error()); - - auto LongIsShort = buildIntegerCompareBefore( - Builder, Anchor, IntegerPredicate::UnsignedLessThan, *OuterExcess, - *InnerBoundary); - if (!LongIsShort) - return std::unexpected(LongIsShort.error()); - auto LongShift = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SHL, *Type, - Parts[0], *OuterExcess); - if (!LongShift) - return std::unexpected(LongShift.error()); - auto LongHigh = buildSelectBefore(Builder, Anchor, *Type, *LongIsShort, - *LongShift, *Zero); - if (!LongHigh) - return std::unexpected(LongHigh.error()); - - auto ResultLow = buildSelectBefore(Builder, Anchor, *Type, *OuterIsShort, - Lower->Low, *Zero); - if (!ResultLow) - return std::unexpected(ResultLow.error()); - auto ResultMiddle = buildSelectBefore(Builder, Anchor, *Type, *OuterIsShort, - Lower->High, *Zero); - if (!ResultMiddle) - return std::unexpected(ResultMiddle.error()); - auto ShiftedHigh = buildSelectBefore(Builder, Anchor, *Type, *OuterIsShort, - *ShortHigh, *LongHigh); - if (!ShiftedHigh) - return std::unexpected(ShiftedHigh.error()); - auto ResultHigh = buildSelectBefore(Builder, Anchor, *Type, *OuterIsZero, - Parts[2], *ShiftedHigh); - if (!ResultHigh) - return std::unexpected(ResultHigh.error()); - return std::vector<Register>{*ResultLow, *ResultMiddle, *ResultHigh}; -} - -[[nodiscard]] Expected<void> lowerWideShiftLeft(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *ValueOperand = Instruction.operandAt(1); - const MachineOperand *AmountOperand = Instruction.operandAt(2); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Value = ValueOperand == nullptr ? nullptr : ValueOperand->getIf<Register>(); - const Register *Amount = AmountOperand == nullptr ? nullptr : AmountOperand->getIf<Register>(); - if (Destination == nullptr || Value == nullptr || Amount == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide shift has invalid register operands"); - auto ValueParts = collectWideValueParts(*Value, Function); - if (!ValueParts) - return std::unexpected(ValueParts.error()); - auto AmountParts = collectWideValueParts(*Amount, Function); - if (!AmountParts) - return std::unexpected(AmountParts.error()); - if (ValueParts->Parts.size() < 2 || AmountParts->Parts.empty()) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide shift has an invalid limb decomposition"); - auto ResultPlan = - wideResultPlan(*Destination, Function, ValueParts->Parts.size()); - if (!ResultPlan) - return std::unexpected(ResultPlan.error()); - - MachineIRBuilder Builder(Function, Target); - Expected<std::vector<Register>> Results = - ValueParts->Parts.size() == 2 - ? emitTwoLimbLeftShift(Builder, Instruction, ValueParts->Parts[0], - ValueParts->Parts[1], AmountParts->Parts[0]) - .transform([](TwoLimbShiftResult Result) { - return std::vector<Register>{Result.Low, Result.High}; - }) - : ValueParts->Parts.size() == 3 - ? emitThreeLimbLeftShift(Builder, Instruction, ValueParts->Parts, - AmountParts->Parts[0]) - : Expected<std::vector<Register>>(std::unexpected( - Error(ErrorCode::Unsupported, - "RISC-V wide shift currently requires two or three 64-bit limbs"))); - if (!Results) - return std::unexpected(Results.error()); - for (std::size_t Index = 0; Index < Results->size(); ++Index) { - auto Replaced = replaceRegisterUses(Function, ResultPlan->Outputs[Index], - (*Results)[Index]); - if (!Replaced) - return std::unexpected(Replaced.error()); - } - if (auto Erased = eraseInstruction(*ResultPlan->Unmerge); !Erased) - return std::unexpected(Erased.error()); - if (ResultPlan->Extension != nullptr) { - if (auto Erased = eraseInstruction(*ResultPlan->Extension); !Erased) - return std::unexpected(Erased.error()); - } - if (auto Erased = eraseInstruction(Instruction); !Erased) - return std::unexpected(Erased.error()); - for (std::size_t Index = 1; Index < AmountParts->Parts.size(); ++Index) { - const MachineOperand *Definition = - Function.registerInfo().getUniqueVRegDef(AmountParts->Parts[Index]); - if (Definition != nullptr && Definition->parent() != nullptr) - AmountParts->Artifacts.push_back( - const_cast<MachineInstr *>(Definition->parent())); - } - ValueParts->Artifacts.insert(ValueParts->Artifacts.end(), - AmountParts->Artifacts.begin(), - AmountParts->Artifacts.end()); - eraseDeadArtifacts(ValueParts->Artifacts, Function); - return {}; -} - -[[nodiscard]] Expected<std::vector<Register>> -emitWideAdd(MachineIRBuilder &Builder, MachineInstr &Anchor, std::span<const Register> Left, - std::span<const Register> Right) { - if (Left.size() != Right.size() || Left.size() < 2) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide add requires matching multi-limb operands"); - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - std::vector<Register> Results; - Results.reserve(Left.size()); - auto Low = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_ADD, *Type, Left[0], Right[0]); - if (!Low) - return std::unexpected(Low.error()); - auto Carry = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::UnsignedLessThan, - *Low, Right[0]); - if (!Carry) - return std::unexpected(Carry.error()); - auto LowCopy = buildUnaryBefore(Builder, Anchor, GenericOpcode::COPY, *Type, *Low); - if (!LowCopy) - return std::unexpected(LowCopy.error()); - Results.push_back(*LowCopy); - for (std::size_t Index = 1; Index < Left.size(); ++Index) { - auto Base = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_ADD, *Type, Left[Index], Right[Index]); - if (!Base) - return std::unexpected(Base.error()); - std::optional<Register> BaseCarry; - if (Index + 1U < Left.size()) { - auto Compared = buildIntegerCompareBefore(Builder, Anchor, - IntegerPredicate::UnsignedLessThan, *Base, - Left[Index]); - if (!Compared) - return std::unexpected(Compared.error()); - BaseCarry = *Compared; - } - auto Sum = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_ADD, *Type, *Base, *Carry); - if (!Sum) - return std::unexpected(Sum.error()); - if (Index + 1U < Left.size()) { - auto Zero = buildIntegerConstantBefore(Builder, Anchor, 64, 0); - if (!Zero) - return std::unexpected(Zero.error()); - auto IsZero = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, *Sum, *Zero); - if (!IsZero) - return std::unexpected(IsZero.error()); - auto CarryFromIncoming = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_AND, *Type, *IsZero, *Carry); - if (!CarryFromIncoming) - return std::unexpected(CarryFromIncoming.error()); - auto NextCarry = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_OR, *Type, *BaseCarry, - *CarryFromIncoming); - if (!NextCarry) - return std::unexpected(NextCarry.error()); - Carry = *NextCarry; - } - auto Copy = buildUnaryBefore(Builder, Anchor, GenericOpcode::COPY, *Type, *Sum); - if (!Copy) - return std::unexpected(Copy.error()); - Results.push_back(*Copy); - } - return Results; -} - -[[nodiscard]] Expected<std::vector<Register>> -emitWideSub(MachineIRBuilder &Builder, MachineInstr &Anchor, std::span<const Register> Left, - std::span<const Register> Right) { - if (Left.size() != Right.size() || Left.size() < 2) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide sub requires matching multi-limb operands"); - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - std::vector<Register> Results; - Results.reserve(Left.size()); - auto Low = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, Left[0], Right[0]); - if (!Low) - return std::unexpected(Low.error()); - Results.push_back(*Low); - auto Borrow = buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::UnsignedLessThan, - Left[0], Right[0]); - if (!Borrow) - return std::unexpected(Borrow.error()); - for (std::size_t Index = 1; Index < Left.size(); ++Index) { - auto Base = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, Left[Index], Right[Index]); - if (!Base) - return std::unexpected(Base.error()); - std::optional<Register> BaseBorrow; - if (Index + 1U < Left.size()) { - auto Compared = buildIntegerCompareBefore(Builder, Anchor, - IntegerPredicate::UnsignedGreaterThan, *Base, - Left[Index]); - if (!Compared) - return std::unexpected(Compared.error()); - BaseBorrow = *Compared; - } - auto Difference = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_SUB, *Type, *Base, *Borrow); - if (!Difference) - return std::unexpected(Difference.error()); - Results.push_back(*Difference); - if (Index + 1U < Left.size()) { - auto Zero = buildIntegerConstantBefore(Builder, Anchor, 64, 0); - if (!Zero) - return std::unexpected(Zero.error()); - auto IsZero = - buildIntegerCompareBefore(Builder, Anchor, IntegerPredicate::Equal, *Base, *Zero); - if (!IsZero) - return std::unexpected(IsZero.error()); - auto BorrowFromIncoming = - buildBinaryBefore(Builder, Anchor, GenericOpcode::G_AND, *Type, *IsZero, *Borrow); - if (!BorrowFromIncoming) - return std::unexpected(BorrowFromIncoming.error()); - auto NextBorrow = buildBinaryBefore(Builder, Anchor, GenericOpcode::G_OR, *Type, *BaseBorrow, - *BorrowFromIncoming); - if (!NextBorrow) - return std::unexpected(NextBorrow.error()); - Borrow = *NextBorrow; - } - } - return Results; -} - -[[nodiscard]] Expected<std::vector<Register>> -emitWideBitwise(MachineIRBuilder &Builder, MachineInstr &Anchor, GenericOpcode Generic, - std::span<const Register> Left, std::span<const Register> Right) { - if (Left.size() != Right.size() || Left.size() < 2) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide bitwise operation requires matching multi-limb operands"); - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - std::vector<Register> Results; - Results.reserve(Left.size()); - for (std::size_t Index = 0; Index < Left.size(); ++Index) { - auto Part = buildBinaryBefore(Builder, Anchor, Generic, *Type, Left[Index], Right[Index]); - if (!Part) - return std::unexpected(Part.error()); - Results.push_back(*Part); - } - return Results; -} - -[[nodiscard]] Expected<void> lowerWideBinary(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - GenericOpcode Generic) { - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *LeftOperand = Instruction.operandAt(1); - const MachineOperand *RightOperand = Instruction.operandAt(2); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - if (Destination == nullptr || Left == nullptr || Right == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide binary instruction has invalid operands"); - auto LeftParts = collectWideValueParts(*Left, Function); - if (!LeftParts) - return std::unexpected(LeftParts.error()); - auto RightParts = collectWideValueParts(*Right, Function); - if (!RightParts) - return std::unexpected(RightParts.error()); - if (LeftParts->Parts.size() != RightParts->Parts.size()) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide binary operands have different limb counts"); - auto ResultPlan = wideResultPlan(*Destination, Function, LeftParts->Parts.size()); - if (!ResultPlan) - return std::unexpected(ResultPlan.error()); - - MachineIRBuilder Builder(Function, Target); - Expected<std::vector<Register>> Results = - Generic == GenericOpcode::G_ADD - ? emitWideAdd(Builder, Instruction, LeftParts->Parts, RightParts->Parts) - : Generic == GenericOpcode::G_SUB - ? emitWideSub(Builder, Instruction, LeftParts->Parts, RightParts->Parts) - : emitWideBitwise(Builder, Instruction, Generic, LeftParts->Parts, RightParts->Parts); - if (!Results) - return std::unexpected(Results.error()); - for (std::size_t Index = 0; Index < Results->size(); ++Index) { - auto Replaced = replaceRegisterUses(Function, ResultPlan->Outputs[Index], (*Results)[Index]); - if (!Replaced) - return std::unexpected(Replaced.error()); - } - if (auto Erased = eraseInstruction(*ResultPlan->Unmerge); !Erased) - return std::unexpected(Erased.error()); - if (ResultPlan->Extension != nullptr) { - if (auto Erased = eraseInstruction(*ResultPlan->Extension); !Erased) - return std::unexpected(Erased.error()); - } - if (auto Erased = eraseInstruction(Instruction); !Erased) - return std::unexpected(Erased.error()); - LeftParts->Artifacts.insert(LeftParts->Artifacts.end(), RightParts->Artifacts.begin(), - RightParts->Artifacts.end()); - eraseDeadArtifacts(LeftParts->Artifacts, Function); - return {}; -} - -[[nodiscard]] bool canLowerNarrowBranchCondition(const MachineInstr &Instruction, - const MachineFunction &Function) { - if (Instruction.opcode() != opcode(GenericOpcode::G_BRCOND) || - Instruction.operandCount() < 2) - return false; - const MachineOperand *ConditionOperand = Instruction.operandAt(0); - const Register *Condition = - ConditionOperand == nullptr ? nullptr : ConditionOperand->getIf<Register>(); - if (Condition == nullptr || !Condition->isVirtual() || - singleRegisterUse(Function, *Condition) != &Instruction) - return false; - const auto ConditionInfo = Function.registerInfo().virtualRegisterInfo(*Condition); - const MachineOperand *Definition = - Function.registerInfo().getUniqueVRegDef(*Condition); - const MachineInstr *Truncation = - Definition == nullptr ? nullptr : Definition->parent(); - if (!ConditionInfo || !ConditionInfo->get().Type || - ConditionInfo->get().Type->scalarBitWidth() != 1 || Truncation == nullptr || - Truncation->opcode() != opcode(GenericOpcode::G_TRUNC) || - Truncation->operandCount() != 2) - return false; - return operandType(Function, *Truncation, 1) - .transform([](LLT Type) { return isScalarGPRType(Type) && Type.scalarBitWidth() == 64; }) - .value_or(false); -} - -[[nodiscard]] Expected<void> -lowerNarrowBranchCondition(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target) { - if (!canLowerNarrowBranchCondition(Instruction, Function)) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V branch condition is not a narrow truncation"); - const MachineOperand *ConditionOperand = Instruction.operandAt(0); - const Register Condition = *ConditionOperand->getIf<Register>(); - const MachineOperand *Definition = - Function.registerInfo().getUniqueVRegDef(Condition); - MachineInstr *Truncation = - const_cast<MachineInstr *>(Definition->parent()); - const Register Source = *Truncation->operandAt(1)->getIf<Register>(); - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - MachineIRBuilder Builder(Function, Target); - auto One = buildIntegerConstantBefore(Builder, Instruction, 64, 1); - if (!One) - return std::unexpected(One.error()); - auto Masked = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_AND, *Type, - Source, *One); - if (!Masked) - return std::unexpected(Masked.error()); - auto Replaced = Instruction.operandAt(0)->setRegister(*Masked); - if (!Replaced) - return std::unexpected(Replaced.error()); - if (Function.registerInfo().useEmpty(Condition)) - return eraseInstruction(*Truncation); - return {}; -} - -struct NarrowPhiIncoming final { - Register Value; - MachineBasicBlock *Block = nullptr; - MachineInstr *ReusableTruncation = nullptr; -}; - -[[nodiscard]] MachineInstr *firstInstructionAfterLeadingCopies(MachineBasicBlock &Block) { - MachineInstr *Instruction = Block.firstInstruction(); - while (Instruction != nullptr && - Instruction->opcode() == opcode(GenericOpcode::COPY)) - Instruction = Instruction->next(); - return Instruction; -} - -[[nodiscard]] Expected<Register> -buildIncomingConversion(MachineFunction &Function, const TargetDescription &Target, - MachineBasicBlock &Block, MachineInstr *Anchor, - GenericOpcode Generic, LLT Type, Register Source) { - auto Result = Function.registerInfo().createVirtualRegister(Type); - if (!Result) - return std::unexpected(Result.error()); - MachineIRBuilder Builder(Function, Target); - auto Point = Anchor == nullptr ? Builder.setInsertPoint(Block) - : Builder.setInsertPointBefore(*Anchor); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(opcode(Generic)); - if (!Pending) - return std::unexpected(Pending.error()); - if (auto Added = Pending->addDef(*Result); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addUse(Source); !Added) - return std::unexpected(Added.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Result; -} - -[[nodiscard]] Expected<void> lowerNarrowPhi(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const std::optional<LLT> OriginalType = resultType(Function, Instruction); - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - if (!OriginalType || Destination == nullptr || OriginalType->scalarBitWidth() >= 32 || - Instruction.operandCount() < 3 || (Instruction.operandCount() % 2U) == 0) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V narrow PHI has invalid operands"); - auto LegalType = LLT::makeAnyScalar(32); - if (!LegalType) - return std::unexpected(LegalType.error()); - - std::vector<NarrowPhiIncoming> Incoming; - Incoming.reserve((Instruction.operandCount() - 1U) / 2U); - for (std::size_t Index = 1; Index < Instruction.operandCount(); Index += 2) { - const MachineOperand *ValueOperand = Instruction.operandAt(Index); - const Register *Value = - ValueOperand == nullptr ? nullptr : ValueOperand->getIf<Register>(); - const MachineOperand *BlockOperand = Instruction.operandAt(Index + 1U); - const auto *Block = - BlockOperand == nullptr ? nullptr : BlockOperand->getIf<BasicBlockOperand>(); - if (Value == nullptr || Block == nullptr || Block->Block == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V narrow PHI has an invalid incoming edge"); - MachineInstr *Reusable = nullptr; - const MachineOperand *Definition = - Value->isVirtual() ? Function.registerInfo().getUniqueVRegDef(*Value) : nullptr; - MachineInstr *Def = - Definition == nullptr ? nullptr : const_cast<MachineInstr *>(Definition->parent()); - if (Def != nullptr && Def->opcode() == opcode(GenericOpcode::G_TRUNC) && - Def->operandCount() == 2 && singleRegisterUse(Function, *Value) == &Instruction && - operandType(Function, *Def, 1) - .transform([](LLT Type) { return Type.scalarBitWidth() >= 32; }) - .value_or(false)) { - Reusable = Def; - } - Incoming.push_back( - NarrowPhiIncoming{.Value = *Value, .Block = Block->Block, - .ReusableTruncation = Reusable}); - } - for (NarrowPhiIncoming &Entry : Incoming) { - if (Entry.ReusableTruncation == nullptr) - continue; - const std::size_t Count = static_cast<std::size_t>(std::ranges::count( - Incoming, Entry.ReusableTruncation, - &NarrowPhiIncoming::ReusableTruncation)); - if (Count != 1) - Entry.ReusableTruncation = nullptr; - } - for (NarrowPhiIncoming &Entry : Incoming) { - if (Entry.ReusableTruncation == nullptr) - continue; - MachineBasicBlock *OriginalBlock = Entry.ReusableTruncation->parent(); - if (OriginalBlock == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V narrow PHI input truncation is detached"); - auto Removed = OriginalBlock->removeInstruction(*Entry.ReusableTruncation); - if (!Removed) - return std::unexpected(Removed.error()); - } - - std::vector<std::pair<MachineBasicBlock *, MachineInstr *>> Anchors; - for (const NarrowPhiIncoming &Entry : Incoming) { - if (std::ranges::find(Anchors, Entry.Block, - &decltype(Anchors)::value_type::first) == Anchors.end()) { - Anchors.emplace_back(Entry.Block, - firstInstructionAfterLeadingCopies(*Entry.Block)); - } - } - std::vector<Register> LegalIncoming; - LegalIncoming.reserve(Incoming.size()); - for (NarrowPhiIncoming &Entry : Incoming) { - const auto AnchorPosition = std::ranges::find( - Anchors, Entry.Block, &decltype(Anchors)::value_type::first); - MachineInstr *Anchor = AnchorPosition->second; - if (Entry.ReusableTruncation != nullptr) { - auto SetType = Function.registerInfo().setType(Entry.Value, *LegalType); - if (!SetType) - return std::unexpected(SetType.error()); - auto Inserted = Anchor == nullptr - ? Entry.Block->appendInstruction(*Entry.ReusableTruncation) - : Entry.Block->insertInstructionBefore( - *Anchor, *Entry.ReusableTruncation); - if (!Inserted) - return std::unexpected(Inserted.error()); - LegalIncoming.push_back(Entry.Value); - continue; - } - const auto ValueInfo = Function.registerInfo().virtualRegisterInfo(Entry.Value); - if (!ValueInfo || !ValueInfo->get().Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V narrow PHI input has no scalar type"); - const std::uint32_t Width = ValueInfo->get().Type->scalarBitWidth(); - const GenericOpcode Conversion = Width < 32 ? GenericOpcode::G_ANYEXT - : Width > 32 ? GenericOpcode::G_TRUNC - : GenericOpcode::COPY; - auto Converted = buildIncomingConversion(Function, Target, *Entry.Block, Anchor, - Conversion, *LegalType, Entry.Value); - if (!Converted) - return std::unexpected(Converted.error()); - LegalIncoming.push_back(*Converted); - } - - auto LegalResult = Function.registerInfo().createVirtualRegister(*LegalType); - if (!LegalResult) - return std::unexpected(LegalResult.error()); - MachineIRBuilder Builder(Function, Target); - auto Point = Builder.setInsertPointBefore(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_PHI)); - if (!Pending) - return std::unexpected(Pending.error()); - if (auto Added = Pending->addDef(*LegalResult); !Added) - return std::unexpected(Added.error()); - for (std::size_t Index = 0; Index < Incoming.size(); ++Index) { - if (auto Added = Pending->addUse(LegalIncoming[Index]); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addBasicBlock(*Incoming[Index].Block); !Added) - return std::unexpected(Added.error()); - } - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - auto Replaced = replaceRegisterUses(Function, *Destination, *LegalResult); - if (!Replaced) - return std::unexpected(Replaced.error()); - return eraseInstruction(Instruction); -} - -[[nodiscard]] Expected<void> lowerWidePhi(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - if (Destination == nullptr || Instruction.operandCount() < 5 || - (Instruction.operandCount() % 2U) == 0) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide PHI has invalid operands"); - - std::vector<WideValueParts> IncomingParts; - std::vector<MachineBasicBlock *> IncomingBlocks; - for (std::size_t Index = 1; Index < Instruction.operandCount(); Index += 2) { - const MachineOperand *ValueOperand = Instruction.operandAt(Index); - const Register *Value = ValueOperand == nullptr ? nullptr : ValueOperand->getIf<Register>(); - const MachineOperand *BlockOperand = Instruction.operandAt(Index + 1U); - const auto *Block = BlockOperand == nullptr ? nullptr : BlockOperand->getIf<BasicBlockOperand>(); - if (Value == nullptr || Block == nullptr || Block->Block == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide PHI has an invalid incoming edge"); - auto Parts = collectWideValueParts(*Value, Function); - if (!Parts) - return std::unexpected(Parts.error()); - if (!IncomingParts.empty() && Parts->Parts.size() != IncomingParts.front().Parts.size()) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V wide PHI incoming values have different limb counts"); - IncomingParts.push_back(std::move(*Parts)); - IncomingBlocks.push_back(Block->Block); - } - const std::size_t PartCount = IncomingParts.front().Parts.size(); - auto ResultPlan = wideResultPlan(*Destination, Function, PartCount); - if (!ResultPlan) - return std::unexpected(ResultPlan.error()); - auto Type = LLT::makeAnyScalar(64); - if (!Type) - return std::unexpected(Type.error()); - MachineIRBuilder Builder(Function, Target); - std::vector<Register> Results; - Results.reserve(PartCount); - for (std::size_t PartIndex = 0; PartIndex < PartCount; ++PartIndex) { - auto Result = Function.registerInfo().createVirtualRegister(*Type); - if (!Result) - return std::unexpected(Result.error()); - auto Point = Builder.setInsertPointBefore(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(opcode(GenericOpcode::G_PHI)); - if (!Pending) - return std::unexpected(Pending.error()); - if (auto Added = Pending->addDef(*Result); !Added) - return std::unexpected(Added.error()); - for (std::size_t IncomingIndex = 0; IncomingIndex < IncomingParts.size(); - ++IncomingIndex) { - if (auto Added = Pending->addUse(IncomingParts[IncomingIndex].Parts[PartIndex]); !Added) - return std::unexpected(Added.error()); - if (auto Added = Pending->addBasicBlock(*IncomingBlocks[IncomingIndex]); !Added) - return std::unexpected(Added.error()); - } - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - Results.push_back(*Result); - } - for (std::size_t Index = 0; Index < PartCount; ++Index) { - auto Replaced = replaceRegisterUses(Function, ResultPlan->Outputs[Index], Results[Index]); - if (!Replaced) - return std::unexpected(Replaced.error()); - } - if (auto Erased = eraseInstruction(*ResultPlan->Unmerge); !Erased) - return std::unexpected(Erased.error()); - if (ResultPlan->Extension != nullptr) { - if (auto Erased = eraseInstruction(*ResultPlan->Extension); !Erased) - return std::unexpected(Erased.error()); - } - if (auto Erased = eraseInstruction(Instruction); !Erased) - return std::unexpected(Erased.error()); - std::vector<MachineInstr *> Artifacts; - for (WideValueParts &Parts : IncomingParts) - Artifacts.insert(Artifacts.end(), Parts.Artifacts.begin(), Parts.Artifacts.end()); - eraseDeadArtifacts(Artifacts, Function); - return {}; -} - -[[nodiscard]] bool canFoldWideMergeImmediately(const MachineInstr &Instruction, - const MachineFunction &Function) { - const MachineOperand *LowOperand = Instruction.operandAt(1); - const Register *Low = LowOperand == nullptr ? nullptr : LowOperand->getIf<Register>(); - if (Low == nullptr) - return false; - const auto LowInfo = Function.registerInfo().virtualRegisterInfo(*Low); - if (!LowInfo || !LowInfo->get().Type) - return false; - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - if (Destination == nullptr) - return false; - auto &RegisterInfo = const_cast<MachineRegisterInfo &>(Function.registerInfo()); - for (MachineOperand &Use : RegisterInfo.useOperands(*Destination)) { - MachineInstr *Trunc = Use.parent(); - if (Trunc == nullptr || Trunc->opcode() != opcode(GenericOpcode::G_TRUNC) || - Trunc->operandAt(1) != &Use) - return false; - const std::optional<LLT> Result = resultType(Function, *Trunc); - if (!Result || Result->scalarBitWidth() > LowInfo->get().Type->scalarBitWidth()) - return false; - } - return true; -} - -struct RV64ScalarLayout final { - std::uint64_t StoreSize = 0; - std::uint64_t AllocationSize = 0; - std::uint64_t ABIAlignment = 0; -}; - -[[nodiscard]] std::optional<RV64ScalarLayout> rv64ScalarLayout(LLT Type) noexcept { - if (Type.isPointer()) { - return Type.addressSpace() == 0 && Type.scalarBitWidth() == 64 - ? std::optional<RV64ScalarLayout>{{8, 8, 8}} - : std::nullopt; - } - if ((!isSupportedIntegerType(Type) && !isRVFloatType(Type)) || Type.scalarBitWidth() == 0) { - return std::nullopt; - } - const std::uint64_t StoreBytes = (Type.scalarBitWidth() + 7U) / 8U; - const std::uint64_t AllocationBytes = std::bit_ceil(StoreBytes); - return RV64ScalarLayout{StoreBytes, AllocationBytes, std::min<std::uint64_t>(AllocationBytes, 8)}; -} - -[[nodiscard]] Expected<const MachineMemOperand *> -createMemoryAccess(MachineFunction &Function, MemoryAccessFlag Access, LLT Type, - std::uint64_t SizeBytes, std::uint64_t AlignmentBytes) { - auto Size = MemorySize::fixed(SizeBytes); - auto Align = Alignment::create(AlignmentBytes); - if (!Size || !Align) - return makeUnexpected(ErrorCode::InternalError, "RISC-V memory facts are invalid"); - MachineMemOperandDesc Description; - Description.Accesses = {Access}; - Description.Size = *Size; - Description.MemoryType = Type; - Description.Align = *Align; - return Function.createMemOperand(std::move(Description)); -} - -struct VAArgPlan final { - Register Destination; - Register ListPointer; - LLT DestinationType; - LLT PointerType; - std::uint64_t Alignment = 0; - RV64ScalarLayout ValueLayout; -}; - -[[nodiscard]] std::optional<VAArgPlan> vaargPlan(const MachineInstr &Instruction, - const MachineFunction &Function) { - if (Instruction.opcode() != opcode(GenericOpcode::G_VAARG) || Instruction.operandCount() != 3 || - !Instruction.memOperands().empty()) { - return std::nullopt; - } - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *ListOperand = Instruction.operandAt(1); - const MachineOperand *AlignmentOperand = Instruction.operandAt(2); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *ListPointer = ListOperand == nullptr ? nullptr : ListOperand->getIf<Register>(); - const std::optional<LLT> DestinationType = resultType(Function, Instruction); - const std::optional<LLT> PointerType = operandType(Function, Instruction, 1); - const auto AlignmentValue = AlignmentOperand == nullptr - ? Expected<std::int64_t>(std::unexpected( - Error(ErrorCode::InvalidArgument, "missing alignment"))) - : signedImmediate(*AlignmentOperand); - const std::optional<RV64ScalarLayout> ValueLayout = - DestinationType ? rv64ScalarLayout(*DestinationType) : std::nullopt; - if (Destination == nullptr || ListPointer == nullptr || !Destination->isVirtual() || - !ListPointer->isVirtual() || !DestinationOperand->isDef() || !ListOperand->isUse() || - !DestinationType || !PointerType || !PointerType->isPointer() || - PointerType->addressSpace() != 0 || PointerType->scalarBitWidth() != 64 || !AlignmentValue || - *AlignmentValue <= 0 || !std::has_single_bit(static_cast<std::uint64_t>(*AlignmentValue)) || - !ValueLayout) { - return std::nullopt; - } - - return VAArgPlan{*Destination, - *ListPointer, - *DestinationType, - *PointerType, - static_cast<std::uint64_t>(*AlignmentValue), - *ValueLayout}; -} - -[[nodiscard]] Expected<void> lowerVAArg(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target) { - const auto Plan = vaargPlan(Instruction, Function); - if (!Plan) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V va_arg legalization requires a scalar result, p0 list, and " - "power-of-two alignment"); - } - - auto ListLoadMemory = - createMemoryAccess(Function, MemoryAccessFlag::Load, Plan->PointerType, 8, 8); - if (!ListLoadMemory) - return std::unexpected(ListLoadMemory.error()); - auto ListStoreMemory = - createMemoryAccess(Function, MemoryAccessFlag::Store, Plan->PointerType, 8, 8); - if (!ListStoreMemory) - return std::unexpected(ListStoreMemory.error()); - - auto CurrentList = Function.registerInfo().createVirtualRegister(Plan->PointerType); - if (!CurrentList) - return std::unexpected(CurrentList.error()); - MachineIRBuilder Builder(Function, Target); - if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) - return std::unexpected(Point.error()); - auto ListLoad = Builder.buildInstr(opcode(GenericOpcode::G_LOAD)); - if (!ListLoad) - return std::unexpected(ListLoad.error()); - if (auto Added = ListLoad->addDef(*CurrentList); !Added) - return std::unexpected(Added.error()); - if (auto Added = ListLoad->addUse(Plan->ListPointer); !Added) - return std::unexpected(Added.error()); - if (auto Added = ListLoad->addMemOperand(**ListLoadMemory); !Added) - return std::unexpected(Added.error()); - if (auto Built = ListLoad->build(); !Built) - return std::unexpected(Built.error()); - - Register ValueAddress = *CurrentList; - if (Plan->Alignment > 1) { - auto AlignmentOffset = - buildIntegerConstantBefore(Builder, Instruction, 64, Plan->Alignment - 1U); - if (!AlignmentOffset) - return std::unexpected(AlignmentOffset.error()); - auto BiasedAddress = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_PTR_ADD, - Plan->PointerType, ValueAddress, *AlignmentOffset); - if (!BiasedAddress) - return std::unexpected(BiasedAddress.error()); - auto AlignmentMask = - buildIntegerConstantBefore(Builder, Instruction, 64, ~(Plan->Alignment - std::uint64_t{1})); - if (!AlignmentMask) - return std::unexpected(AlignmentMask.error()); - auto AlignedAddress = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_PTRMASK, - Plan->PointerType, *BiasedAddress, *AlignmentMask); - if (!AlignedAddress) - return std::unexpected(AlignedAddress.error()); - ValueAddress = *AlignedAddress; - } - - auto Increment = - buildIntegerConstantBefore(Builder, Instruction, 64, Plan->ValueLayout.AllocationSize); - if (!Increment) - return std::unexpected(Increment.error()); - auto NextList = buildBinaryBefore(Builder, Instruction, GenericOpcode::G_PTR_ADD, - Plan->PointerType, ValueAddress, *Increment); - if (!NextList) - return std::unexpected(NextList.error()); - auto ListStore = Builder.buildInstr(opcode(GenericOpcode::G_STORE)); - if (!ListStore) - return std::unexpected(ListStore.error()); - if (auto Added = ListStore->addUse(*NextList); !Added) - return std::unexpected(Added.error()); - if (auto Added = ListStore->addUse(Plan->ListPointer); !Added) - return std::unexpected(Added.error()); - if (auto Added = ListStore->addMemOperand(**ListStoreMemory); !Added) - return std::unexpected(Added.error()); - if (auto Built = ListStore->build(); !Built) - return std::unexpected(Built.error()); - - if (!Function.registerInfo().useEmpty(Plan->Destination)) { - auto ValueLoadMemory = - createMemoryAccess(Function, MemoryAccessFlag::Load, Plan->DestinationType, - Plan->ValueLayout.StoreSize, Plan->ValueLayout.ABIAlignment); - if (!ValueLoadMemory) - return std::unexpected(ValueLoadMemory.error()); - auto ValueLoad = Builder.buildInstr(opcode(GenericOpcode::G_LOAD)); - if (!ValueLoad) - return std::unexpected(ValueLoad.error()); - if (auto Added = ValueLoad->addDef(Plan->Destination); !Added) - return std::unexpected(Added.error()); - if (auto Added = ValueLoad->addUse(ValueAddress); !Added) - return std::unexpected(Added.error()); - if (auto Added = ValueLoad->addMemOperand(**ValueLoadMemory); !Added) - return std::unexpected(Added.error()); - if (auto Built = ValueLoad->build(); !Built) - return std::unexpected(Built.error()); - } - return eraseInstruction(Instruction); -} - -struct VACopyPlan final { - Register Destination; - Register Source; - LLT PointerType; -}; - -[[nodiscard]] std::optional<VACopyPlan> vacopyPlan(const MachineInstr &Instruction, - const MachineFunction &Function) { - if (Instruction.opcode() != opcode(GenericOpcode::G_INTRINSIC_W_SIDE_EFFECTS) || - Instruction.operandCount() != 3 || !Instruction.memOperands().empty()) { - return std::nullopt; - } - const MachineOperand *IntrinsicIDOperand = Instruction.operandAt(0); - const MachineOperand *DestinationOperand = Instruction.operandAt(1); - const MachineOperand *SourceOperand = Instruction.operandAt(2); - const auto *Intrinsic = - IntrinsicIDOperand == nullptr ? nullptr : IntrinsicIDOperand->getIf<IntrinsicOperand>(); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); - const std::optional<LLT> DestinationType = operandType(Function, Instruction, 1); - const std::optional<LLT> SourceType = operandType(Function, Instruction, 2); - if (Intrinsic == nullptr || Intrinsic->ID != intrinsicID(GenericIntrinsic::VACopy) || - Destination == nullptr || Source == nullptr || !Destination->isVirtual() || - !Source->isVirtual() || !DestinationOperand->isUse() || !SourceOperand->isUse() || - !DestinationType || !SourceType || *DestinationType != *SourceType || - !DestinationType->isPointer() || DestinationType->addressSpace() != 0 || - DestinationType->scalarBitWidth() != 64) { - return std::nullopt; - } - return VACopyPlan{*Destination, *Source, *DestinationType}; -} - -[[nodiscard]] Expected<void> lowerVACopy(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target) { - const auto Plan = vacopyPlan(Instruction, Function); - if (!Plan) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V va_copy legalization requires two p0 operands"); - } - auto Size = MemorySize::fixed(8); - auto Align = Alignment::create(8); - if (!Size || !Align) - return makeUnexpected(ErrorCode::InternalError, "RISC-V pointer memory facts are invalid"); - - MachineMemOperandDesc LoadDescription; - LoadDescription.Accesses = {MemoryAccessFlag::Load}; - LoadDescription.Size = *Size; - LoadDescription.MemoryType = Plan->PointerType; - LoadDescription.Align = *Align; - 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 = Plan->PointerType; - StoreDescription.Align = *Align; - auto StoreMemory = Function.createMemOperand(std::move(StoreDescription)); - if (!StoreMemory) - return std::unexpected(StoreMemory.error()); - - auto Value = Function.registerInfo().createVirtualRegister(Plan->PointerType); - if (!Value) - return std::unexpected(Value.error()); - MachineIRBuilder Builder(Function, Target); - if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) - return std::unexpected(Point.error()); - - auto Load = Builder.buildInstr(opcode(GenericOpcode::G_LOAD)); - if (!Load) - return std::unexpected(Load.error()); - if (auto Added = Load->addDef(*Value); !Added) - return std::unexpected(Added.error()); - if (auto Added = Load->addUse(Plan->Source); !Added) - return std::unexpected(Added.error()); - if (auto Added = Load->addMemOperand(**LoadMemory); !Added) - return std::unexpected(Added.error()); - if (auto Built = Load->build(); !Built) - return std::unexpected(Built.error()); - - auto Store = Builder.buildInstr(opcode(GenericOpcode::G_STORE)); - if (!Store) - return std::unexpected(Store.error()); - if (auto Added = Store->addUse(*Value); !Added) - return std::unexpected(Added.error()); - if (auto Added = Store->addUse(Plan->Destination); !Added) - return std::unexpected(Added.error()); - if (auto Added = Store->addMemOperand(**StoreMemory); !Added) - return std::unexpected(Added.error()); - if (auto Built = Store->build(); !Built) - return std::unexpected(Built.error()); - return eraseInstruction(Instruction); -} - -class RISCVLegalizerInfo final : public LegalizerInfo { -public: - explicit RISCVLegalizerInfo(RISCVSubtargetFeatures Features) : Features(Features) {} - - [[nodiscard]] Expected<LegalizationAction> classify(const MachineInstr &Instruction, - const MachineFunction &Function, - const TargetDescription &) const override { - if (!Features.has(RISCVExtension::I)) { - return makeUnexpected(ErrorCode::InternalError, - "RISC-V legalizer requires the base I extension"); - } - if (!isGenericOpcode(Instruction.opcode())) - return LegalizationAction::Legal; - - const GenericOpcode Generic = static_cast<GenericOpcode>(Instruction.opcode().value()); - const std::optional<LLT> Type = resultType(Function, Instruction); - switch (Generic) { - case GenericOpcode::G_INTRINSIC_W_SIDE_EFFECTS: - return vacopyPlan(Instruction, Function) ? LegalizationAction::Custom - : LegalizationAction::Unsupported; - case GenericOpcode::G_VAARG: - return vaargPlan(Instruction, Function) ? LegalizationAction::Custom - : LegalizationAction::Unsupported; - case GenericOpcode::G_FRAME_INDEX: - return Type && Type->isPointer() && Type->addressSpace() == 0 && Type->scalarBitWidth() == 64 - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - case GenericOpcode::G_BR: - return LegalizationAction::Legal; - case GenericOpcode::G_BRCOND: { - const std::optional<LLT> Condition = operandType(Function, Instruction, 0); - if (!Condition || !isScalarGPRType(*Condition)) - return LegalizationAction::Unsupported; - return canLowerNarrowBranchCondition(Instruction, Function) ? LegalizationAction::Custom - : LegalizationAction::Legal; - } - case GenericOpcode::G_PHI: { - if (!Type) - return LegalizationAction::Unsupported; - for (std::size_t Index = 1; Index < Instruction.operandCount(); Index += 2) { - const std::optional<LLT> Incoming = operandType(Function, Instruction, Index); - if (!Incoming || *Incoming != *Type) - return LegalizationAction::Unsupported; - } - if ((Type->isInteger() || Type->isAnyScalar()) && Type->scalarBitWidth() > 64) - return LegalizationAction::Custom; - if ((Type->isInteger() || Type->isAnyScalar()) && Type->scalarBitWidth() < 32) - return LegalizationAction::Custom; - if (!isSupportedIntegerType(*Type) && !isRVFloatType(*Type)) - return LegalizationAction::Unsupported; - return LegalizationAction::Legal; - } - case GenericOpcode::G_IMPLICIT_DEF: - return Type && (isSupportedIntegerType(*Type) || isRVFloatType(*Type)) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - case GenericOpcode::G_STORE: - if (Instruction.operandCount() == 0) - return LegalizationAction::Unsupported; - if (const std::optional<LLT> ValueType = registerType(Function, *Instruction.operandAt(0)); - ValueType) { - const std::optional<LLT> AddressType = operandType(Function, Instruction, 1); - if (!AddressType || !AddressType->isPointer() || AddressType->addressSpace() != 0 || - AddressType->scalarBitWidth() != 64) { - return LegalizationAction::Unsupported; - } - if (Instruction.memOperands().size() == 1 && - Instruction.memOperands().front()->isAtomic()) { - const std::uint64_t Bytes = ValueType->scalarBitWidth() / 8U; - return isSupportedIntegerType(*ValueType) && - isSupportedAtomicMemoryAccess(Instruction, AtomicOperation::Store, Bytes) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - if ((isSupportedIntegerType(*ValueType) || isRVFloatType(*ValueType)) && - isSupportedNonAtomicMemoryAccess(Instruction) && - memoryMatchesType(Instruction, *ValueType)) { - return LegalizationAction::Legal; - } - } - return LegalizationAction::Unsupported; - case GenericOpcode::G_SEXTLOAD: - case GenericOpcode::G_ZEXTLOAD: { - const auto Bytes = memoryAccessBytes(Instruction); - const std::optional<LLT> AddressType = operandType(Function, Instruction, 1); - return Type && Bytes && AddressType && isSupportedIntegerType(*Type) && - AddressType->isPointer() && AddressType->addressSpace() == 0 && - AddressType->scalarBitWidth() == 64 && - *Bytes < ((Type->scalarBitWidth() + 7U) / 8U) && - (*Bytes == 1 || *Bytes == 2 || *Bytes == 4) && - isSupportedNonAtomicMemoryAccess(Instruction) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FPEXTLOAD: { - const auto Bytes = memoryAccessBytes(Instruction); - const std::optional<LLT> AddressType = operandType(Function, Instruction, 1); - return Type && Bytes && AddressType && Type->isFloat() && Type->scalarBitWidth() == 64 && - AddressType->isPointer() && AddressType->addressSpace() == 0 && - AddressType->scalarBitWidth() == 64 && *Bytes == 4 && - isSupportedNonAtomicMemoryAccess(Instruction) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FPTRUNCSTORE: { - const std::optional<LLT> ValueType = operandType(Function, Instruction, 0); - const std::optional<LLT> AddressType = operandType(Function, Instruction, 1); - const auto Bytes = memoryAccessBytes(Instruction); - return ValueType && AddressType && Bytes && ValueType->isFloat() && - ValueType->scalarBitWidth() == 64 && AddressType->isPointer() && - AddressType->addressSpace() == 0 && AddressType->scalarBitWidth() == 64 && - *Bytes == 4 && isSupportedNonAtomicMemoryAccess(Instruction) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_LOAD: - if (Type) { - const std::optional<LLT> AddressType = operandType(Function, Instruction, 1); - if (!AddressType || !AddressType->isPointer() || AddressType->addressSpace() != 0 || - AddressType->scalarBitWidth() != 64) { - return LegalizationAction::Unsupported; - } - if (Instruction.memOperands().size() == 1 && - Instruction.memOperands().front()->isAtomic()) { - const std::uint64_t Bytes = Type->scalarBitWidth() / 8U; - return isSupportedIntegerType(*Type) && - isSupportedAtomicMemoryAccess(Instruction, AtomicOperation::Load, Bytes) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - if ((isSupportedIntegerType(*Type) || isRVFloatType(*Type)) && - isSupportedNonAtomicMemoryAccess(Instruction) && - memoryMatchesType(Instruction, *Type)) { - return LegalizationAction::Legal; - } - } - return LegalizationAction::Unsupported; - case GenericOpcode::G_FADD: - case GenericOpcode::G_FSUB: - case GenericOpcode::G_FMUL: - case GenericOpcode::G_FDIV: { - const std::optional<LLT> Left = operandType(Function, Instruction, 1); - const std::optional<LLT> Right = operandType(Function, Instruction, 2); - return Type && Left && Right && isRVFloatType(*Type) && *Left == *Type && *Right == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FMA: { - const std::optional<LLT> First = operandType(Function, Instruction, 1); - const std::optional<LLT> Second = operandType(Function, Instruction, 2); - const std::optional<LLT> Third = operandType(Function, Instruction, 3); - return Type && First && Second && Third && isRVFloatType(*Type) && *First == *Type && - *Second == *Type && *Third == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FNEG: - case GenericOpcode::G_FABS: - case GenericOpcode::G_FSQRT: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - return Type && Source && isRVFloatType(*Type) && *Source == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FCOPYSIGN: { - const std::optional<LLT> Left = operandType(Function, Instruction, 1); - const std::optional<LLT> Right = operandType(Function, Instruction, 2); - return Type && Left && Right && isRVFloatType(*Type) && *Left == *Type && *Right == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FCONSTANT: { - const MachineOperand *Immediate = Instruction.operandAt(1); - const auto *Floating = - Immediate == nullptr ? nullptr : Immediate->getIf<FloatingImmediateOperand>(); - const std::optional<FloatSemantics> Semantics = Type ? Type->floatSemantics() : std::nullopt; - return Type && isRVFloatType(*Type) && Floating != nullptr && Semantics && - Floating->Bits.semantics() == *Semantics - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_ASSERT_SEXT: - case GenericOpcode::G_ASSERT_ZEXT: - case GenericOpcode::G_ASSERT_ALIGN: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - return Type && Source && *Type == *Source && isScalarGPRType(*Type) && - Instruction.operandCount() == 3 - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FREEZE: - case GenericOpcode::G_CONSTANT_FOLD_BARRIER: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - return Type && Source && *Type == *Source && - (isSupportedIntegerType(*Type) || isRVFloatType(*Type)) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_PTRTOINT: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - return Type && Source && Type->isInteger() && Type->scalarBitWidth() == 64 && - Source->isPointer() && Source->scalarBitWidth() == 64 - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_INTTOPTR: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - return Type && Source && Type->isPointer() && Type->scalarBitWidth() == 64 && - Source->isInteger() && Source->scalarBitWidth() == 64 - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_BITCAST: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - const bool IntegerFloatPair = - Type && Source && Type->scalarBitWidth() == Source->scalarBitWidth() && - ((Type->isFloat() && Source->isInteger()) || (Type->isInteger() && Source->isFloat())) && - (Type->scalarBitWidth() == 32 || Type->scalarBitWidth() == 64); - return IntegerFloatPair ? LegalizationAction::Legal : LegalizationAction::Unsupported; - } - case GenericOpcode::G_ANYEXT: - case GenericOpcode::G_SEXT: - case GenericOpcode::G_ZEXT: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - if (!Type || !Source) - return LegalizationAction::Unsupported; - if (!isScalarIntegerExtension(*Type, *Source)) { - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *Destination = DestinationOperand == nullptr - ? nullptr - : DestinationOperand->getIf<Register>(); - MachineInstr *OnlyUse = Destination == nullptr - ? nullptr - : singleUseInstruction(Function, *Destination); - return (Type->isInteger() || Type->isAnyScalar()) && - (Source->isInteger() || Source->isAnyScalar()) && - Type->scalarBitWidth() > 64 && - Source->scalarBitWidth() < Type->scalarBitWidth() && - OnlyUse != nullptr && - OnlyUse->opcode() == opcode(GenericOpcode::G_UNMERGE_VALUES) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - const MachineOperand *SourceOperand = Instruction.operandAt(1); - const Register *SourceRegister = - SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); - const MachineOperand *SourceDefinition = - SourceRegister == nullptr || !SourceRegister->isVirtual() - ? nullptr - : Function.registerInfo().getUniqueVRegDef(*SourceRegister); - const MachineInstr *SourceDef = - SourceDefinition == nullptr ? nullptr : SourceDefinition->parent(); - if (SourceDef != nullptr && SourceDef->opcode() == opcode(GenericOpcode::G_CONSTANT)) - return LegalizationAction::Custom; - if (Generic == GenericOpcode::G_ANYEXT && - narrowAnyExtBinaryPlan(Instruction, Function).has_value()) - return LegalizationAction::Custom; - return LegalizationAction::Legal; - } - case GenericOpcode::G_TRUNC: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - if (Type && Source && (Type->isInteger() || Type->isAnyScalar()) && - (Source->isInteger() || Source->isAnyScalar()) && Type->scalarBitWidth() > 64 && - Source->scalarBitWidth() > Type->scalarBitWidth()) - return LegalizationAction::Legal; - return Type && Source && isSupportedIntegerScalar(*Type) && - isSupportedIntegerScalar(*Source) && Type->scalarBitWidth() != 0 && - Source->scalarBitWidth() >= Type->scalarBitWidth() - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_MERGE_VALUES: - case GenericOpcode::G_UNMERGE_VALUES: { - if (Instruction.operandCount() < 3) - return LegalizationAction::Unsupported; - const std::size_t SmallTypeIndex = - Generic == GenericOpcode::G_MERGE_VALUES ? 1 : 0; - const std::size_t BigTypeIndex = Generic == GenericOpcode::G_MERGE_VALUES - ? 0 - : Instruction.operandCount() - 1U; - const std::optional<LLT> SmallType = operandType(Function, Instruction, SmallTypeIndex); - const std::optional<LLT> BigType = operandType(Function, Instruction, BigTypeIndex); - if (SmallType && BigType && Generic == GenericOpcode::G_MERGE_VALUES && - BigType->scalarBitWidth() > 64 && - !canFoldWideMergeImmediately(Instruction, Function)) - return LegalizationAction::Legal; - return SmallType && BigType && - (SmallType->isInteger() || SmallType->isAnyScalar()) && - (BigType->isInteger() || BigType->isAnyScalar()) && - SmallType->scalarBitWidth() != 0 && BigType->scalarBitWidth() != 0 - ? LegalizationAction::Custom - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_SEXT_INREG: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - const MachineOperand *WidthOperand = Instruction.operandAt(2); - const auto Width = - WidthOperand == nullptr - ? Expected<std::int64_t>(std::unexpected( - Error(ErrorCode::InvalidArgument, "missing in-register extension width"))) - : signedImmediate(*WidthOperand); - return Type && Source && *Type == *Source && isScalarGPRType(*Type) && Width && *Width > 0 && - static_cast<std::uint64_t>(*Width) <= Type->scalarBitWidth() - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_ICMP: { - const MachineOperand *Predicate = Instruction.operandAt(1); - const auto *IntegerPredicate = - Predicate == nullptr ? nullptr : Predicate->getIf<PredicateOperand>(); - const std::optional<LLT> Left = operandType(Function, Instruction, 2); - const std::optional<LLT> Right = operandType(Function, Instruction, 3); - if (!Type || !isSupportedIntegerScalar(*Type) || IntegerPredicate == nullptr || - IntegerPredicate->integerPredicate() == nullptr || !Left || !Right || *Left != *Right || - !isSupportedIntegerType(*Left)) { - return LegalizationAction::Unsupported; - } - return Type->scalarBitWidth() == 64 && Left->scalarBitWidth() == 64 - ? LegalizationAction::Legal - : LegalizationAction::Custom; - } - case GenericOpcode::G_FCMP: { - const MachineOperand *Predicate = Instruction.operandAt(1); - const auto *FloatingPredicate = - Predicate == nullptr ? nullptr : Predicate->getIf<PredicateOperand>(); - const std::optional<LLT> Left = operandType(Function, Instruction, 2); - const std::optional<LLT> Right = operandType(Function, Instruction, 3); - return Type && isI1Type(*Type) && FloatingPredicate != nullptr && - FloatingPredicate->floatingPredicate() != nullptr && Left && Right && - *Left == *Right && isRVFloatType(*Left) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FPEXT: - case GenericOpcode::G_FPTRUNC: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - const bool IsExtension = Generic == GenericOpcode::G_FPEXT; - return Type && Source && isRVFloatType(*Type) && isRVFloatType(*Source) && - ((IsExtension && Source->scalarBitWidth() == 32 && - Type->scalarBitWidth() == 64) || - (!IsExtension && Source->scalarBitWidth() == 64 && - Type->scalarBitWidth() == 32)) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FPTOSI: - case GenericOpcode::G_FPTOUI: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - return Type && Source && isSupportedIntegerType(*Type) && isRVFloatType(*Source) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_SITOFP: - case GenericOpcode::G_UITOFP: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - return Type && Source && isRVFloatType(*Type) && isSupportedIntegerType(*Source) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FMINNUM: - case GenericOpcode::G_FMAXNUM: { - const std::optional<LLT> Left = operandType(Function, Instruction, 1); - const std::optional<LLT> Right = operandType(Function, Instruction, 2); - return Type && Left && Right && isRVFloatType(*Type) && *Left == *Type && *Right == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_SELECT: { - const std::optional<LLT> Condition = operandType(Function, Instruction, 1); - const std::optional<LLT> TrueValue = operandType(Function, Instruction, 2); - const std::optional<LLT> FalseValue = operandType(Function, Instruction, 3); - return Type && Condition && TrueValue && FalseValue && isScalarGPRType(*Condition) && - *TrueValue == *Type && *FalseValue == *Type && - (isSupportedIntegerType(*Type) || isRVFloatType(*Type)) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_PTRMASK: { - const std::optional<LLT> Pointer = operandType(Function, Instruction, 1); - const std::optional<LLT> Mask = operandType(Function, Instruction, 2); - return Type && Pointer && Mask && Type->isPointer() && Type->addressSpace() == 0 && - Type->scalarBitWidth() == 64 && *Pointer == *Type && - isSupportedIntegerScalar(*Mask) && Mask->scalarBitWidth() == 64 - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_ABS: { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - return Type && Source && *Source == *Type && isSupportedIntegerScalar(*Type) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_SDIVREM: - case GenericOpcode::G_UDIVREM: { - const std::optional<LLT> Remainder = operandType(Function, Instruction, 1); - const std::optional<LLT> Left = operandType(Function, Instruction, 2); - const std::optional<LLT> Right = operandType(Function, Instruction, 3); - return Type && Remainder && Left && Right && isSupportedIntegerScalar(*Type) && - *Remainder == *Type && *Left == *Type && *Right == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_SMULH: - case GenericOpcode::G_UMULH: { - const std::optional<LLT> Left = operandType(Function, Instruction, 1); - const std::optional<LLT> Right = operandType(Function, Instruction, 2); - return Type && Left && Right && isNativeIntegerScalar(*Type) && *Left == *Type && - *Right == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_SMIN: - case GenericOpcode::G_SMAX: - case GenericOpcode::G_UMIN: - case GenericOpcode::G_UMAX: { - const std::optional<LLT> Left = operandType(Function, Instruction, 1); - const std::optional<LLT> Right = operandType(Function, Instruction, 2); - return Type && Left && Right && isSupportedIntegerScalar(*Type) && *Left == *Type && - *Right == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_UADDO: - case GenericOpcode::G_USUBO: - case GenericOpcode::G_SADDO: - case GenericOpcode::G_SSUBO: - case GenericOpcode::G_UMULO: - case GenericOpcode::G_SMULO: { - const std::optional<LLT> Overflow = operandType(Function, Instruction, 1); - const std::optional<LLT> Left = operandType(Function, Instruction, 2); - const std::optional<LLT> Right = operandType(Function, Instruction, 3); - return Type && Overflow && Left && Right && isNativeIntegerScalar(*Type) && - isI1Type(*Overflow) && *Left == *Type && *Right == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_UADDE: - case GenericOpcode::G_USUBE: - case GenericOpcode::G_SADDE: - case GenericOpcode::G_SSUBE: { - const std::optional<LLT> CarryOut = operandType(Function, Instruction, 1); - const std::optional<LLT> Left = operandType(Function, Instruction, 2); - const std::optional<LLT> Right = operandType(Function, Instruction, 3); - const std::optional<LLT> CarryIn = operandType(Function, Instruction, 4); - return Type && CarryOut && Left && Right && CarryIn && isNativeIntegerScalar(*Type) && - isI1Type(*CarryOut) && isI1Type(*CarryIn) && *Left == *Type && *Right == *Type - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_ATOMICRMW_XCHG: - case GenericOpcode::G_ATOMICRMW_ADD: - case GenericOpcode::G_ATOMICRMW_SUB: - case GenericOpcode::G_ATOMICRMW_AND: - case GenericOpcode::G_ATOMICRMW_OR: - case GenericOpcode::G_ATOMICRMW_XOR: - case GenericOpcode::G_ATOMICRMW_MAX: - case GenericOpcode::G_ATOMICRMW_MIN: - case GenericOpcode::G_ATOMICRMW_UMAX: - case GenericOpcode::G_ATOMICRMW_UMIN: { - const std::optional<LLT> Address = operandType(Function, Instruction, 1); - const std::optional<LLT> Value = operandType(Function, Instruction, 2); - const std::uint64_t Bytes = Type ? Type->scalarBitWidth() / 8U : 0; - return Type && Address && Value && isNativeIntegerScalar(*Type) && *Value == *Type && - Address->isPointer() && Address->addressSpace() == 0 && - Address->scalarBitWidth() == 64 && - isSupportedAtomicMemoryAccess(Instruction, AtomicOperation::ReadModifyWrite, - Bytes) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_ATOMIC_CMPXCHG: { - const std::optional<LLT> Address = operandType(Function, Instruction, 1); - const std::optional<LLT> Compare = operandType(Function, Instruction, 2); - const std::optional<LLT> NewValue = operandType(Function, Instruction, 3); - if (!Type || !Address || !Compare || !NewValue || *Compare != *Type || *NewValue != *Type || - (!Type->isInteger() && !Type->isAnyScalar()) || - (Type->scalarBitWidth() != 8 && Type->scalarBitWidth() != 16 && - Type->scalarBitWidth() != 32 && Type->scalarBitWidth() != 64) || - !Address->isPointer() || Address->addressSpace() != 0 || - Address->scalarBitWidth() != 64 || - !isSupportedAtomicContainerAccess(Instruction, AtomicOperation::CompareExchange, - (Type->scalarBitWidth() + 7U) / 8U)) { - return LegalizationAction::Unsupported; - } - return Type->scalarBitWidth() == 64 ? LegalizationAction::Legal : LegalizationAction::Custom; - } - case GenericOpcode::G_ATOMIC_CMPXCHG_WITH_SUCCESS: { - const std::optional<LLT> Success = operandType(Function, Instruction, 1); - const std::optional<LLT> Address = operandType(Function, Instruction, 2); - const std::optional<LLT> Compare = operandType(Function, Instruction, 3); - const std::optional<LLT> NewValue = operandType(Function, Instruction, 4); - return Type && Success && Address && Compare && NewValue && isI1Type(*Success) && - *Compare == *Type && *NewValue == *Type && - (Type->isInteger() || Type->isAnyScalar()) && - (Type->scalarBitWidth() == 8 || Type->scalarBitWidth() == 16 || - Type->scalarBitWidth() == 32 || Type->scalarBitWidth() == 64) && - Address->isPointer() && Address->addressSpace() == 0 && - Address->scalarBitWidth() == 64 && - isSupportedAtomicContainerAccess(Instruction, AtomicOperation::CompareExchange, - (Type->scalarBitWidth() + 7U) / 8U) - ? LegalizationAction::Custom - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_FENCE: { - const MachineOperand *OrderingOperand = Instruction.operandAt(0); - const MachineOperand *ScopeOperand = Instruction.operandAt(1); - const auto Ordering = OrderingOperand == nullptr - ? Expected<std::int64_t>(std::unexpected( - Error(ErrorCode::InvalidArgument, "missing fence ordering"))) - : signedImmediate(*OrderingOperand); - const auto Scope = ScopeOperand == nullptr - ? Expected<std::int64_t>(std::unexpected( - Error(ErrorCode::InvalidArgument, "missing fence scope"))) - : signedImmediate(*ScopeOperand); - return Ordering && Scope && *Ordering >= 4 && *Ordering <= 7 && *Scope == 0 - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_CONSTANT_POOL: { - const MachineOperand *PoolOperand = Instruction.operandAt(1); - const auto *Pool = PoolOperand == nullptr ? nullptr : PoolOperand->getIf<ConstantPoolOperand>(); - return Type && Type->isPointer() && Type->addressSpace() == 0 && - Type->scalarBitWidth() == 64 && Pool != nullptr && - Function.constantPool().entry(Pool->Index) != nullptr - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_GLOBAL_VALUE: { - const MachineOperand *GlobalOperand = Instruction.operandAt(1); - const auto *Global = GlobalOperand == nullptr - ? nullptr - : GlobalOperand->getIf<GlobalSymbolOperand>(); - return Type && Type->isPointer() && Type->addressSpace() == 0 && - Type->scalarBitWidth() == 64 && Global != nullptr && - !Global->Name.empty() - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_BRINDIRECT: - return operandType(Function, Instruction, 0) - .transform([](LLT Value) { - return Value.isPointer() && Value.addressSpace() == 0 && - Value.scalarBitWidth() == 64; - }) - .value_or(false) - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - case GenericOpcode::G_TRAP: - case GenericOpcode::G_DEBUGTRAP: - return Instruction.operandCount() == 0 ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - case GenericOpcode::G_UBSANTRAP: - return Instruction.operandCount() == 1 ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - case GenericOpcode::G_CONSTANT: { - const MachineOperand *Immediate = Instruction.operandAt(1); - if (!Type || Immediate == nullptr || - (Immediate->kind() != MachineOperand::Kind::Immediate && - Immediate->kind() != MachineOperand::Kind::IntegerImmediate)) - return LegalizationAction::Unsupported; - if (Type->scalarBitWidth() > 64) - return wideConstantUnmergePlan(Instruction, Function).has_value() - ? LegalizationAction::Custom - : LegalizationAction::Unsupported; - if ((Type->isInteger() || Type->isAnyScalar()) && Type->scalarBitWidth() == 64) { - auto Value = signedImmediate(*Immediate); - if (Value && shouldUseRV64IntegerConstantPool(*Value)) - return LegalizationAction::Custom; - } - return isSupportedIntegerType(*Type) ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_ADD: - case GenericOpcode::G_SUB: - case GenericOpcode::G_MUL: - case GenericOpcode::G_SDIV: - case GenericOpcode::G_UDIV: - case GenericOpcode::G_SREM: - case GenericOpcode::G_UREM: - case GenericOpcode::G_AND: - case GenericOpcode::G_OR: - case GenericOpcode::G_XOR: - case GenericOpcode::G_SHL: - case GenericOpcode::G_LSHR: - case GenericOpcode::G_ASHR: { - const std::optional<LLT> Left = operandType(Function, Instruction, 1); - const std::optional<LLT> Right = operandType(Function, Instruction, 2); - if (!Type || !Left || !Right || *Left != *Type || *Right != *Type || - (!Type->isInteger() && !Type->isAnyScalar())) - return LegalizationAction::Unsupported; - if (Type->scalarBitWidth() > 64) { - return Generic == GenericOpcode::G_ADD || Generic == GenericOpcode::G_SUB || - Generic == GenericOpcode::G_AND || Generic == GenericOpcode::G_OR || - Generic == GenericOpcode::G_XOR || Generic == GenericOpcode::G_SHL - ? LegalizationAction::Custom - : LegalizationAction::Unsupported; - } - if (Generic == GenericOpcode::G_MUL && !Features.has(RISCVExtension::M)) { - if (Type->scalarBitWidth() == 64) - return LegalizationAction::Libcall; - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - MachineInstr *Extension = - Destination == nullptr ? nullptr : singleRegisterUse(Function, *Destination); - const std::optional<LLT> ExtensionType = - Extension == nullptr ? std::nullopt : resultType(Function, *Extension); - if (Extension != nullptr && Extension->opcode() == opcode(GenericOpcode::G_ANYEXT) && - ExtensionType && ExtensionType->scalarBitWidth() == 64) { - return LegalizationAction::Legal; - } - return LegalizationAction::Custom; - } - return isSupportedIntegerScalar(*Type) ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - case GenericOpcode::G_PTR_ADD: { - const std::optional<LLT> Pointer = operandType(Function, Instruction, 1); - const std::optional<LLT> Offset = operandType(Function, Instruction, 2); - return Type && Pointer && Offset && Type->isPointer() && Type->addressSpace() == 0 && - Type->scalarBitWidth() == 64 && *Pointer == *Type && - isSupportedIntegerScalar(*Offset) && Offset->scalarBitWidth() == 64 - ? LegalizationAction::Legal - : LegalizationAction::Unsupported; - } - default: - break; - } - - // Keep the unsupported policy mechanically synchronized with the complete - // target-independent opcode inventory. Supported cases return above. - switch (Generic) { -#define RCC_CORE_OPCODE(Name) -#define RCC_GENERIC_OPCODE(Name) case GenericOpcode::Name: -#include "rcc/CodeGen/GenericOpcode.def" -#undef RCC_GENERIC_OPCODE -#undef RCC_CORE_OPCODE - return LegalizationAction::Unsupported; - case GenericOpcode::Invalid: - case GenericOpcode::CORE_PSEUDO_END: - case GenericOpcode::G_GENERIC_END: - default: - break; - } - return makeUnexpected(ErrorCode::InternalError, - "RISC-V legalizer received an invalid generic opcode value"); - } - - [[nodiscard]] Expected<void> lower(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target, - LegalizationAction Action) const override { - if (Action == LegalizationAction::Custom && - Instruction.opcode() == opcode(GenericOpcode::G_INTRINSIC_W_SIDE_EFFECTS)) { - return lowerVACopy(Instruction, Function, Target); - } - if (Action == LegalizationAction::Custom && - Instruction.opcode() == opcode(GenericOpcode::G_VAARG)) { - return lowerVAArg(Instruction, Function, Target); - } - if (Instruction.opcode() == opcode(GenericOpcode::G_MUL) && !Features.has(RISCVExtension::M)) { - if (Action == LegalizationAction::Custom) - return widenNarrowMultiply(Instruction, Function, Target); - if (Action == LegalizationAction::Libcall) - return lowerMultiplyLibcall(Instruction, Function, Target, - Features.has(RISCVExtension::D) ? CallingConvention::LP64D - : CallingConvention::LP64); - } - if (Action == LegalizationAction::Custom && - resultType(Function, Instruction) - .transform([](LLT Type) { return Type.scalarBitWidth() > 64; }) - .value_or(false)) { - const Opcode Value = Instruction.opcode(); - if (Value == opcode(GenericOpcode::G_ADD) || Value == opcode(GenericOpcode::G_SUB) || - Value == opcode(GenericOpcode::G_AND) || Value == opcode(GenericOpcode::G_OR) || - Value == opcode(GenericOpcode::G_XOR)) { - return lowerWideBinary(Instruction, Function, Target, - static_cast<GenericOpcode>(Value.value())); - } - if (Value == opcode(GenericOpcode::G_SHL)) - return lowerWideShiftLeft(Instruction, Function, Target); - if (Value == opcode(GenericOpcode::G_PHI)) - return lowerWidePhi(Instruction, Function, Target); - } - if (Action == LegalizationAction::Custom && - Instruction.opcode() == opcode(GenericOpcode::G_MERGE_VALUES)) - return lowerMergeValues(Instruction, Function, Target); - if (Action == LegalizationAction::Custom && - Instruction.opcode() == opcode(GenericOpcode::G_UNMERGE_VALUES)) - return lowerUnmergeValues(Instruction, Function, Target); - if (Action == LegalizationAction::Custom && - Instruction.opcode() == opcode(GenericOpcode::G_BRCOND)) - return lowerNarrowBranchCondition(Instruction, Function, Target); - if (Action == LegalizationAction::Custom && - Instruction.opcode() == opcode(GenericOpcode::G_PHI)) - return lowerNarrowPhi(Instruction, Function, Target); - if (Action == LegalizationAction::Custom && - Instruction.opcode() == opcode(GenericOpcode::G_ICMP)) - return lowerIntegerCompare(Instruction, Function, Target); - if (Action == LegalizationAction::Custom && - Instruction.opcode() == opcode(GenericOpcode::G_ATOMIC_CMPXCHG)) - return lowerAtomicCompareExchange(Instruction, Function, Target); - if (Action == LegalizationAction::Custom && - Instruction.opcode() == opcode(GenericOpcode::G_ATOMIC_CMPXCHG_WITH_SUCCESS)) - return lowerAtomicCompareExchangeWithSuccess(Instruction, Function, Target); - if (Action == LegalizationAction::Custom && - (Instruction.opcode() == opcode(GenericOpcode::G_ANYEXT) || - Instruction.opcode() == opcode(GenericOpcode::G_SEXT) || - Instruction.opcode() == opcode(GenericOpcode::G_ZEXT))) { - if (Instruction.opcode() == opcode(GenericOpcode::G_ANYEXT) && - narrowAnyExtBinaryPlan(Instruction, Function).has_value()) - return foldNarrowAnyExtBinary(Instruction, Function, Target); - return foldConstantExtension( - Instruction, Function, - static_cast<GenericOpcode>(Instruction.opcode().value())); - } - if (Action == LegalizationAction::Custom && - Instruction.opcode() == opcode(GenericOpcode::G_CONSTANT)) { - const MachineOperand *Immediate = Instruction.operandAt(1); - const std::optional<LLT> Type = resultType(Function, Instruction); - if (Immediate != nullptr && Type && (Type->isInteger() || Type->isAnyScalar()) && - Type->scalarBitWidth() == 64) { - auto Value = signedImmediate(*Immediate); - if (Value && shouldUseRV64IntegerConstantPool(*Value)) - return lowerConstantToPool(Instruction, Function, Target); - } - return foldWideConstant(Instruction, Function, Target); - } - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V legalizer has no lowering rule for this instruction"); - } - -private: - RISCVSubtargetFeatures Features; -}; - -class RISCVRegisterBankInfo final : public RegisterBankInfo { -public: - [[nodiscard]] Expected<RegisterBankID> selectBank(const MachineFunction &, Register, - const VirtualRegisterInfo &Info, - const MachineInstr *, - const TargetDescription &) const override { - if (!Info.Type) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V register-bank selection requires a virtual-register type"); - } - if (isScalarGPRType(*Info.Type)) { - return RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::GPR)); - } - if (isRVFloatType(*Info.Type)) - return RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::FPR)); - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V RV64GC has no register bank for virtual register type '" + - Info.Type->toString() + "'"); - } - - [[nodiscard]] Expected<void> - constrainInstruction(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target) const override { - std::vector<std::pair<Register, RegisterClassID>> PlannedClasses; - for (const MachineOperand &Operand : Instruction.operands()) { - const Register *Reg = Operand.getIf<Register>(); - if (Reg == nullptr || !Reg->isVirtual()) - continue; - auto Required = requiredRegisterClass(Function, *Reg, Target); - if (!Required) - return std::unexpected(Required.error()); - if (std::ranges::find_if(PlannedClasses, [Reg](const auto &Entry) { - return Entry.first == *Reg; - }) == PlannedClasses.end()) - PlannedClasses.emplace_back(*Reg, *Required); - } - for (const auto &[Reg, Required] : PlannedClasses) { - auto Info = Function.registerInfo().virtualRegisterInfo(Reg); - if (!Info) - return std::unexpected(Info.error()); - if (Info->get().RegClass) - continue; - auto SetClass = Function.registerInfo().setRegisterClass(Reg, Required); - if (!SetClass) - return std::unexpected(SetClass.error()); - } - return {}; - } - - [[nodiscard]] Expected<void> validateInstruction(const MachineInstr &Instruction, - const MachineFunction &Function, - const TargetDescription &Target) const override { - for (const MachineOperand &Operand : Instruction.operands()) { - const Register *Reg = Operand.getIf<Register>(); - if (Reg == nullptr || !Reg->isVirtual()) - continue; - auto Required = requiredRegisterClass(Function, *Reg, Target); - if (!Required) - return std::unexpected(Required.error()); - } - return {}; - } -}; - -class RISCVInstructionSelector final : public InstructionSelector { -public: - [[nodiscard]] Expected<void> - constrainSelectedInstruction(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target) const override { - return registerBankInfo().constrainInstruction(Instruction, Function, Target); - } - - [[nodiscard]] Expected<void> canSelect(const MachineInstr &Instruction, - const MachineFunction &Function, - const TargetDescription &Target) const override { - if (Instruction.opcode() == targetOpcode(InstructionOpcode::G_SLLW)) { - const std::optional<LLT> Type = resultType(Function, Instruction); - const std::optional<LLT> Left = operandType(Function, Instruction, 1); - const std::optional<LLT> Right = operandType(Function, Instruction, 2); - return Type && Left && Right && isScalarGPRType(*Type) && - Type->scalarBitWidth() == 64 && *Left == *Type && *Right == *Type - ? Expected<void>{} - : makeUnexpected(ErrorCode::Unsupported, - "RISC-V G_SLLW requires three 64-bit GPR operands"); - } - if (!isGenericOpcode(Instruction.opcode())) - return {}; - const GenericOpcode Generic = static_cast<GenericOpcode>(Instruction.opcode().value()); - const std::optional<LLT> Type = resultType(Function, Instruction); - const auto Integer = [Type] { return Type && isSupportedIntegerType(*Type); }; - const auto Float = [Type] { return Type && isRVFloatType(*Type); }; - auto Classification = legalizerInfo().classify(Instruction, Function, Target); - if (!Classification) - return std::unexpected(Classification.error()); - if (*Classification != LegalizationAction::Legal) { - std::string Message = "RISC-V selector cannot select opcode '" + - std::string(getGenericOpcodeName(Generic)) + "'"; - if (Type) - Message += " with result type '" + Type->toString() + "'"; - Message += ": the legalization rule explicitly marks this combination unsupported"; - return makeUnexpected(ErrorCode::Unsupported, std::move(Message)); - } - switch (Generic) { - case GenericOpcode::G_ADD: - case GenericOpcode::G_SUB: - case GenericOpcode::G_MUL: - case GenericOpcode::G_SDIV: - case GenericOpcode::G_UDIV: - case GenericOpcode::G_SREM: - case GenericOpcode::G_UREM: - case GenericOpcode::G_AND: - case GenericOpcode::G_OR: - case GenericOpcode::G_XOR: - case GenericOpcode::G_SHL: - case GenericOpcode::G_LSHR: - case GenericOpcode::G_ASHR: - case GenericOpcode::G_PTR_ADD: - case GenericOpcode::G_PTRMASK: - case GenericOpcode::G_CONSTANT: - if (!Integer()) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V selector requires an i1-i64 integer or 64-bit pointer value"); - if (Generic == GenericOpcode::G_CONSTANT) { - const MachineOperand *Immediate = Instruction.operandAt(1); - if (Immediate == nullptr || (Immediate->kind() != MachineOperand::Kind::Immediate && - Immediate->kind() != MachineOperand::Kind::IntegerImmediate)) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V integer constant requires an immediate operand"); - } - return {}; - case GenericOpcode::G_LOAD: - case GenericOpcode::G_SEXTLOAD: - case GenericOpcode::G_ZEXTLOAD: - case GenericOpcode::G_FPEXTLOAD: - case GenericOpcode::G_FPTRUNCSTORE: - case GenericOpcode::G_STORE: - case GenericOpcode::G_BR: - case GenericOpcode::G_BRCOND: - return {}; - case GenericOpcode::G_FADD: - case GenericOpcode::G_FSUB: - case GenericOpcode::G_FMUL: - case GenericOpcode::G_FDIV: - case GenericOpcode::G_FMA: - case GenericOpcode::G_FSQRT: - case GenericOpcode::G_FNEG: - case GenericOpcode::G_FABS: - case GenericOpcode::G_FCOPYSIGN: - if (!Float()) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V selector requires f32 or f64 floating values"); - return {}; - case GenericOpcode::G_FCONSTANT: { - const MachineOperand *Immediate = Instruction.operandAt(1); - const auto *Floating = - Immediate == nullptr ? nullptr : Immediate->getIf<FloatingImmediateOperand>(); - const std::optional<FloatSemantics> Semantics = Type ? Type->floatSemantics() : std::nullopt; - if (!Float() || Floating == nullptr || !Semantics || Floating->Bits.semantics() != *Semantics) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V selector requires a matching floating constant"); - return {}; - } - case GenericOpcode::G_ANYEXT: - case GenericOpcode::G_ASSERT_SEXT: - case GenericOpcode::G_ASSERT_ZEXT: - case GenericOpcode::G_ASSERT_ALIGN: - case GenericOpcode::G_FREEZE: - case GenericOpcode::G_CONSTANT_FOLD_BARRIER: - case GenericOpcode::G_PTRTOINT: - case GenericOpcode::G_INTTOPTR: - case GenericOpcode::G_BITCAST: - case GenericOpcode::G_TRUNC: - case GenericOpcode::G_SEXT: - case GenericOpcode::G_SEXT_INREG: - case GenericOpcode::G_ZEXT: - case GenericOpcode::G_ICMP: - case GenericOpcode::G_FCMP: - case GenericOpcode::G_FPEXT: - case GenericOpcode::G_FPTRUNC: - case GenericOpcode::G_FPTOSI: - case GenericOpcode::G_FPTOUI: - case GenericOpcode::G_SITOFP: - case GenericOpcode::G_UITOFP: - case GenericOpcode::G_FMINNUM: - case GenericOpcode::G_FMAXNUM: - case GenericOpcode::G_SDIVREM: - case GenericOpcode::G_UDIVREM: - case GenericOpcode::G_SMULH: - case GenericOpcode::G_UMULH: - case GenericOpcode::G_SMIN: - case GenericOpcode::G_SMAX: - case GenericOpcode::G_UMIN: - case GenericOpcode::G_UMAX: - case GenericOpcode::G_SELECT: - case GenericOpcode::G_ABS: - case GenericOpcode::G_UADDO: - case GenericOpcode::G_USUBO: - case GenericOpcode::G_SADDO: - case GenericOpcode::G_SSUBO: - case GenericOpcode::G_UMULO: - case GenericOpcode::G_SMULO: - case GenericOpcode::G_UADDE: - case GenericOpcode::G_USUBE: - case GenericOpcode::G_SADDE: - case GenericOpcode::G_SSUBE: - case GenericOpcode::G_ATOMICRMW_XCHG: - case GenericOpcode::G_ATOMICRMW_ADD: - case GenericOpcode::G_ATOMICRMW_SUB: - case GenericOpcode::G_ATOMICRMW_AND: - case GenericOpcode::G_ATOMICRMW_OR: - case GenericOpcode::G_ATOMICRMW_XOR: - case GenericOpcode::G_ATOMICRMW_MAX: - case GenericOpcode::G_ATOMICRMW_MIN: - case GenericOpcode::G_ATOMICRMW_UMAX: - case GenericOpcode::G_ATOMICRMW_UMIN: - case GenericOpcode::G_FENCE: - case GenericOpcode::G_BRINDIRECT: - case GenericOpcode::G_TRAP: - case GenericOpcode::G_DEBUGTRAP: - case GenericOpcode::G_UBSANTRAP: - case GenericOpcode::G_PHI: - case GenericOpcode::G_IMPLICIT_DEF: - return {}; - default: - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V instruction selector has no rule for generic opcode"); - } - } - - [[nodiscard]] Expected<void> select(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target) const override { - auto Match = canSelect(Instruction, Function, Target); - if (!Match) - return std::unexpected(Match.error()); - if (Instruction.opcode() == targetOpcode(InstructionOpcode::G_SLLW)) - return Instruction.setOpcode(targetOpcode(InstructionOpcode::SLLW)); - const GenericOpcode Generic = static_cast<GenericOpcode>(Instruction.opcode().value()); - const std::optional<LLT> Type = resultType(Function, Instruction); - - const auto SelectDirect = [&Instruction](InstructionOpcode Selected) { - return Instruction.setOpcode(targetOpcode(Selected)); - }; - switch (Generic) { - case GenericOpcode::G_ADD: - return selectIntegerBinary(Instruction, Type, InstructionOpcode::ADD, - InstructionOpcode::ADDW); - case GenericOpcode::G_SUB: - return selectIntegerBinary(Instruction, Type, InstructionOpcode::SUB, - InstructionOpcode::SUBW); - case GenericOpcode::G_MUL: - return selectIntegerBinary(Instruction, Type, InstructionOpcode::MUL, - InstructionOpcode::MULW); - case GenericOpcode::G_SDIV: - return selectNormalizedIntegerBinary(Instruction, Function, Target, Type, - InstructionOpcode::DIV, InstructionOpcode::DIVW, true, - true); - case GenericOpcode::G_UDIV: - return selectNormalizedIntegerBinary(Instruction, Function, Target, Type, - InstructionOpcode::DIVU, InstructionOpcode::DIVUW, false, - true); - case GenericOpcode::G_SREM: - return selectNormalizedIntegerBinary(Instruction, Function, Target, Type, - InstructionOpcode::REM, InstructionOpcode::REMW, true, - true); - case GenericOpcode::G_UREM: - return selectNormalizedIntegerBinary(Instruction, Function, Target, Type, - InstructionOpcode::REMU, InstructionOpcode::REMUW, false, - true); - case GenericOpcode::G_SDIVREM: - return selectDivRem(Instruction, Function, Target, Type, true); - case GenericOpcode::G_UDIVREM: - return selectDivRem(Instruction, Function, Target, Type, false); - case GenericOpcode::G_AND: - return SelectDirect(InstructionOpcode::AND); - case GenericOpcode::G_OR: - return SelectDirect(InstructionOpcode::OR); - case GenericOpcode::G_XOR: - return SelectDirect(InstructionOpcode::XOR); - case GenericOpcode::G_SHL: - return selectIntegerBinary(Instruction, Type, InstructionOpcode::SLL, - InstructionOpcode::SLLW); - case GenericOpcode::G_LSHR: - return selectNormalizedIntegerBinary(Instruction, Function, Target, Type, - InstructionOpcode::SRL, InstructionOpcode::SRLW, false, - false); - case GenericOpcode::G_ASHR: - return selectNormalizedIntegerBinary(Instruction, Function, Target, Type, - InstructionOpcode::SRA, InstructionOpcode::SRAW, true, - false); - case GenericOpcode::G_PTR_ADD: - return SelectDirect(InstructionOpcode::ADD); - case GenericOpcode::G_PTRMASK: - return SelectDirect(InstructionOpcode::AND); - case GenericOpcode::G_LOAD: - return selectLoad(Instruction, Type); - case GenericOpcode::G_SEXTLOAD: - return selectExtLoad(Instruction, Type, true); - case GenericOpcode::G_ZEXTLOAD: - return selectExtLoad(Instruction, Type, false); - case GenericOpcode::G_FPEXTLOAD: - return selectFloatExtLoad(Instruction, Function, Target); - case GenericOpcode::G_STORE: - return selectStore(Instruction, Function); - case GenericOpcode::G_FPTRUNCSTORE: - return selectFloatTruncStore(Instruction, Function, Target); - case GenericOpcode::G_BR: - return selectBranch(Instruction); - case GenericOpcode::G_BRCOND: - return selectConditionalBranch(Instruction); - case GenericOpcode::G_CONSTANT: - return selectIntegerConstant(Instruction, Function, Target, Type); - case GenericOpcode::G_FCONSTANT: - return selectFloatingConstant(Instruction, Function, Target, Type); - case GenericOpcode::G_ASSERT_SEXT: - case GenericOpcode::G_ASSERT_ZEXT: - case GenericOpcode::G_ASSERT_ALIGN: - if (Instruction.operandCount() == 3) { - auto EraseImmediate = Instruction.eraseOperand(2); - if (!EraseImmediate) - return std::unexpected(EraseImmediate.error()); - } - return Instruction.setOpcode(opcode(GenericOpcode::COPY)); - case GenericOpcode::G_FREEZE: - case GenericOpcode::G_CONSTANT_FOLD_BARRIER: - case GenericOpcode::G_PTRTOINT: - case GenericOpcode::G_INTTOPTR: - return Instruction.setOpcode(opcode(GenericOpcode::COPY)); - case GenericOpcode::G_BITCAST: - return selectBitcast(Instruction, Function); - case GenericOpcode::G_FADD: - return selectRoundingBinary(Instruction, Type, InstructionOpcode::FADD_S, - InstructionOpcode::FADD_D); - case GenericOpcode::G_FSUB: - return selectRoundingBinary(Instruction, Type, InstructionOpcode::FSUB_S, - InstructionOpcode::FSUB_D); - case GenericOpcode::G_FMUL: - return selectRoundingBinary(Instruction, Type, InstructionOpcode::FMUL_S, - InstructionOpcode::FMUL_D); - case GenericOpcode::G_FDIV: - return selectRoundingBinary(Instruction, Type, InstructionOpcode::FDIV_S, - InstructionOpcode::FDIV_D); - case GenericOpcode::G_FMA: - return selectRoundingTernary(Instruction, Type, InstructionOpcode::FMADD_S, - InstructionOpcode::FMADD_D); - case GenericOpcode::G_FSQRT: - return selectRoundingUnary(Instruction, Type, InstructionOpcode::FSQRT_S, - InstructionOpcode::FSQRT_D); - case GenericOpcode::G_FNEG: - return selectSignInjection(Instruction, Type, InstructionOpcode::FSGNJN_S, - InstructionOpcode::FSGNJN_D); - case GenericOpcode::G_FABS: - return selectSignInjection(Instruction, Type, InstructionOpcode::FSGNJX_S, - InstructionOpcode::FSGNJX_D); - case GenericOpcode::G_FCOPYSIGN: - return selectFloatingMinMax(Instruction, Type, InstructionOpcode::FSGNJ_S, - InstructionOpcode::FSGNJ_D); - case GenericOpcode::G_ANYEXT: - case GenericOpcode::G_SEXT: - case GenericOpcode::G_ZEXT: - return selectIntegerExtension(Instruction, Function, Target, Generic, Type); - case GenericOpcode::G_TRUNC: - return Instruction.setOpcode(opcode(GenericOpcode::COPY)); - case GenericOpcode::G_SEXT_INREG: - return selectSignExtendInRegister(Instruction, Function, Target, Type); - case GenericOpcode::G_ICMP: - return selectIntegerCompare(Instruction, Function, Target); - case GenericOpcode::G_FCMP: - return selectFloatingCompare(Instruction, Function, Target); - case GenericOpcode::G_FPEXT: - return selectRoundingUnary(Instruction, Type, InstructionOpcode::FCVT_D_S, - InstructionOpcode::FCVT_D_S); - case GenericOpcode::G_FPTRUNC: - return selectRoundingUnary(Instruction, Type, InstructionOpcode::FCVT_S_D, - InstructionOpcode::FCVT_S_D); - case GenericOpcode::G_FPTOSI: - case GenericOpcode::G_FPTOUI: - return selectFloatToInteger(Instruction, Function, Type, Generic == GenericOpcode::G_FPTOUI); - case GenericOpcode::G_SITOFP: - case GenericOpcode::G_UITOFP: - return selectIntegerToFloat(Instruction, Function, Target, Type, - Generic == GenericOpcode::G_UITOFP); - case GenericOpcode::G_FMINNUM: - return selectFloatingMinMax(Instruction, Type, InstructionOpcode::FMIN_S, - InstructionOpcode::FMIN_D); - case GenericOpcode::G_FMAXNUM: - return selectFloatingMinMax(Instruction, Type, InstructionOpcode::FMAX_S, - InstructionOpcode::FMAX_D); - case GenericOpcode::G_SMULH: - return selectMultiplyHigh(Instruction, Function, Target, Type, true); - case GenericOpcode::G_UMULH: - return selectMultiplyHigh(Instruction, Function, Target, Type, false); - case GenericOpcode::G_SMIN: - case GenericOpcode::G_SMAX: - case GenericOpcode::G_UMIN: - case GenericOpcode::G_UMAX: - return selectIntegerMinMax(Instruction, Function, Target, Type, Generic); - case GenericOpcode::G_SELECT: - return selectValue(Instruction, Function, Target, Type); - case GenericOpcode::G_ABS: - return selectAbsoluteValue(Instruction, Function, Target, Type); - case GenericOpcode::G_UADDO: - case GenericOpcode::G_USUBO: - case GenericOpcode::G_SADDO: - case GenericOpcode::G_SSUBO: - case GenericOpcode::G_UMULO: - case GenericOpcode::G_SMULO: - return selectOverflow(Instruction, Function, Target, Type, Generic); - case GenericOpcode::G_UADDE: - case GenericOpcode::G_USUBE: - case GenericOpcode::G_SADDE: - case GenericOpcode::G_SSUBE: - return selectExtendedOverflow(Instruction, Function, Target, Type, Generic); - case GenericOpcode::G_ATOMICRMW_XCHG: - case GenericOpcode::G_ATOMICRMW_ADD: - case GenericOpcode::G_ATOMICRMW_SUB: - case GenericOpcode::G_ATOMICRMW_AND: - case GenericOpcode::G_ATOMICRMW_OR: - case GenericOpcode::G_ATOMICRMW_XOR: - case GenericOpcode::G_ATOMICRMW_MAX: - case GenericOpcode::G_ATOMICRMW_MIN: - case GenericOpcode::G_ATOMICRMW_UMAX: - case GenericOpcode::G_ATOMICRMW_UMIN: - return selectAtomicRMW(Instruction, Function, Target, Type, Generic); - case GenericOpcode::G_FENCE: - return selectFence(Instruction); - case GenericOpcode::G_BRINDIRECT: - return selectIndirectBranch(Instruction); - case GenericOpcode::G_TRAP: - case GenericOpcode::G_DEBUGTRAP: - case GenericOpcode::G_UBSANTRAP: - return selectTrap(Instruction); - case GenericOpcode::G_PHI: - return Instruction.setOpcode(opcode(GenericOpcode::PHI)); - case GenericOpcode::G_IMPLICIT_DEF: - return Instruction.setOpcode(opcode(GenericOpcode::IMPLICIT_DEF)); - case GenericOpcode::COPY: - case GenericOpcode::IMPLICIT_DEF: - case GenericOpcode::PHI: - case GenericOpcode::REG_SEQUENCE: - case GenericOpcode::INSERT_SUBREG: - case GenericOpcode::EXTRACT_SUBREG: - case GenericOpcode::SUBREG_TO_REG: - case GenericOpcode::KILL: - case GenericOpcode::BUNDLE: - case GenericOpcode::INLINEASM: - case GenericOpcode::CORE_PSEUDO_END: - break; - default: - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V instruction selector has no rule for generic opcode"); - } - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V instruction selector has no rule for generic opcode"); - } - -private: - [[nodiscard]] static Expected<void> replaceImmediate(MachineInstr &Instruction, std::size_t Index, - std::int64_t Value) { - if (Instruction.operandAt(Index) == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V instruction is missing an immediate operand"); - auto Erased = Instruction.eraseOperand(Index); - if (!Erased) - return std::unexpected(Erased.error()); - return Instruction.insertOperand(Index, MachineOperand::immediate(Value)); - } - - [[nodiscard]] static Expected<MachineInstr *> - buildBinaryAfter(MachineIRBuilder &Builder, MachineInstr &Anchor, InstructionOpcode Opcode, - Register Def, Register Left, Register Right) { - auto Point = Builder.setInsertPointAfter(Anchor); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(targetOpcode(Opcode)); - if (!Pending) - return std::unexpected(Pending.error()); - auto DefResult = Pending->addDef(Def); - if (!DefResult) - return std::unexpected(DefResult.error()); - auto LeftResult = Pending->addUse(Left); - if (!LeftResult) - return std::unexpected(LeftResult.error()); - auto RightResult = Pending->addUse(Right); - if (!RightResult) - return std::unexpected(RightResult.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Built; - } - - [[nodiscard]] static Expected<MachineInstr *> - buildImmediateAfter(MachineIRBuilder &Builder, MachineInstr &Anchor, InstructionOpcode Opcode, - Register Def, Register Source, std::int64_t Value) { - auto Point = Builder.setInsertPointAfter(Anchor); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(targetOpcode(Opcode)); - if (!Pending) - return std::unexpected(Pending.error()); - auto DefResult = Pending->addDef(Def); - if (!DefResult) - return std::unexpected(DefResult.error()); - auto SourceResult = Pending->addUse(Source); - if (!SourceResult) - return std::unexpected(SourceResult.error()); - auto ImmediateResult = Pending->addImmediate(Value); - if (!ImmediateResult) - return std::unexpected(ImmediateResult.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Built; - } - - [[nodiscard]] static Expected<MachineInstr *> - buildImmediateBefore(MachineIRBuilder &Builder, MachineInstr &Anchor, InstructionOpcode Opcode, - Register Def, Register Source, std::int64_t Value) { - auto Point = Builder.setInsertPointBefore(Anchor); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(targetOpcode(Opcode)); - if (!Pending) - return std::unexpected(Pending.error()); - auto DefResult = Pending->addDef(Def); - if (!DefResult) - return std::unexpected(DefResult.error()); - auto SourceResult = Pending->addUse(Source); - if (!SourceResult) - return std::unexpected(SourceResult.error()); - auto ImmediateResult = Pending->addImmediate(Value); - if (!ImmediateResult) - return std::unexpected(ImmediateResult.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Built; - } - - [[nodiscard]] static Expected<Register> - normalizeIntegerBefore(MachineInstr &Anchor, MachineFunction &Function, - const TargetDescription &Target, Register Source, LLT Type, bool Signed) { - if (!isSupportedIntegerScalar(Type)) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V integer normalization requires a scalar i1-i64 type"); - const std::uint32_t Bits = Type.scalarBitWidth(); - if (Bits == 64) - return Source; - - MachineIRBuilder Builder(Function, Target); - if (Signed && Bits == 32) { - auto Result = createTemporary(Function, Type, Target); - if (!Result) - return std::unexpected(Result.error()); - auto Extended = - buildImmediateBefore(Builder, Anchor, InstructionOpcode::ADDIW, *Result, Source, 0); - if (!Extended) - return std::unexpected(Extended.error()); - return *Result; - } - - const std::uint32_t ContainerBits = Bits < 32 ? 32 : 64; - const std::int64_t Shift = static_cast<std::int64_t>(ContainerBits - Bits); - auto Shifted = createTemporary(Function, Type, Target); - auto Result = createTemporary(Function, Type, Target); - if (!Shifted || !Result) - return std::unexpected(!Shifted ? Shifted.error() : Result.error()); - const InstructionOpcode ShiftLeft = - ContainerBits == 32 ? InstructionOpcode::SLLIW : InstructionOpcode::SLLI; - const InstructionOpcode ShiftRight = - Signed ? (ContainerBits == 32 ? InstructionOpcode::SRAIW : InstructionOpcode::SRAI) - : (ContainerBits == 32 ? InstructionOpcode::SRLIW : InstructionOpcode::SRLI); - auto Left = buildImmediateBefore(Builder, Anchor, ShiftLeft, *Shifted, Source, Shift); - if (!Left) - return std::unexpected(Left.error()); - auto Right = buildImmediateBefore(Builder, Anchor, ShiftRight, *Result, *Shifted, Shift); - if (!Right) - return std::unexpected(Right.error()); - return *Result; - } - - [[nodiscard]] static Expected<MachineInstr *> buildUnaryAfter(MachineIRBuilder &Builder, - MachineInstr &Anchor, - InstructionOpcode Opcode, - Register Def, Register Source) { - auto Point = Builder.setInsertPointAfter(Anchor); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr(targetOpcode(Opcode)); - if (!Pending) - return std::unexpected(Pending.error()); - auto DefResult = Pending->addDef(Def); - if (!DefResult) - return std::unexpected(DefResult.error()); - auto SourceResult = Pending->addUse(Source); - if (!SourceResult) - return std::unexpected(SourceResult.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - return *Built; - } - - [[nodiscard]] static Expected<MachineInstr *> - buildGPRSelectAfter(MachineIRBuilder &Builder, MachineInstr &Anchor, MachineFunction &Function, - const TargetDescription &Target, LLT ValueType, Register Destination, - Register Condition, Register TrueValue, Register FalseValue) { - auto IntegerType = LLT::makeInteger(ValueType.scalarBitWidth()); - if (!IntegerType) - return std::unexpected(IntegerType.error()); - auto Mask = createTemporary(Function, *IntegerType, Target); - auto Difference = createTemporary(Function, *IntegerType, Target); - auto MaskedDifference = createTemporary(Function, *IntegerType, Target); - if (!Mask || !Difference || !MaskedDifference) { - return std::unexpected( - (!Mask ? Mask.error() : (!Difference ? Difference.error() : MaskedDifference.error()))); - } - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - const InstructionOpcode Subtract = - ValueType.scalarBitWidth() == 32 ? InstructionOpcode::SUBW : InstructionOpcode::SUB; - auto MaskInstruction = buildBinaryAfter(Builder, Anchor, Subtract, *Mask, - Register::fromPhysical(*Zero), Condition); - if (!MaskInstruction) - return std::unexpected(MaskInstruction.error()); - auto DifferenceInstruction = buildBinaryAfter( - Builder, **MaskInstruction, InstructionOpcode::XOR, *Difference, TrueValue, FalseValue); - if (!DifferenceInstruction) - return std::unexpected(DifferenceInstruction.error()); - auto MaskInstructionResult = - buildBinaryAfter(Builder, **DifferenceInstruction, InstructionOpcode::AND, - *MaskedDifference, *Difference, *Mask); - if (!MaskInstructionResult) - return std::unexpected(MaskInstructionResult.error()); - return buildBinaryAfter(Builder, **MaskInstructionResult, InstructionOpcode::XOR, Destination, - FalseValue, *MaskedDifference); - } - - [[nodiscard]] static Expected<void> selectDivRem(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - std::optional<LLT> Type, bool Signed) { - const MachineOperand *RemainderOperand = Instruction.operandAt(1); - const MachineOperand *LeftOperand = Instruction.operandAt(2); - const MachineOperand *RightOperand = Instruction.operandAt(3); - const Register *Remainder = - RemainderOperand == nullptr ? nullptr : RemainderOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - if (!Type || Remainder == nullptr || Left == nullptr || Right == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V division-with-remainder has invalid operands"); - const Register RemainderRegister = *Remainder; - auto LeftRegister = normalizeIntegerBefore(Instruction, Function, Target, *Left, *Type, Signed); - auto RightRegister = - normalizeIntegerBefore(Instruction, Function, Target, *Right, *Type, Signed); - if (!LeftRegister || !RightRegister) - return std::unexpected(!LeftRegister ? LeftRegister.error() : RightRegister.error()); - auto RemoveRemainder = Instruction.eraseOperand(1); - if (!RemoveRemainder) - return std::unexpected(RemoveRemainder.error()); - auto SetLeft = Instruction.operandAt(1)->setRegister(*LeftRegister); - if (!SetLeft) - return std::unexpected(SetLeft.error()); - auto SetRight = Instruction.operandAt(2)->setRegister(*RightRegister); - if (!SetRight) - return std::unexpected(SetRight.error()); - const bool IsWord = Type->scalarBitWidth() <= 32; - const InstructionOpcode Division = - IsWord ? (Signed ? InstructionOpcode::DIVW : InstructionOpcode::DIVUW) - : (Signed ? InstructionOpcode::DIV : InstructionOpcode::DIVU); - const InstructionOpcode RemainderOpcode = - IsWord ? (Signed ? InstructionOpcode::REMW : InstructionOpcode::REMUW) - : (Signed ? InstructionOpcode::REM : InstructionOpcode::REMU); - auto SetDivision = Instruction.setOpcode(targetOpcode(Division)); - if (!SetDivision) - return std::unexpected(SetDivision.error()); - MachineIRBuilder Builder(Function, Target); - auto BuiltRemainder = buildBinaryAfter(Builder, Instruction, RemainderOpcode, RemainderRegister, - *LeftRegister, *RightRegister); - if (!BuiltRemainder) - return std::unexpected(BuiltRemainder.error()); - return {}; - } - - [[nodiscard]] static Expected<void> selectMultiplyHigh(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - std::optional<LLT> Type, bool Signed) { - if (!Type) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V high multiplication has no result type"); - if (Type->scalarBitWidth() == 64) { - return Instruction.setOpcode( - targetOpcode(Signed ? InstructionOpcode::MULH : InstructionOpcode::MULHU)); - } - - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *LeftOperand = Instruction.operandAt(1); - const MachineOperand *RightOperand = Instruction.operandAt(2); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - if (Destination == nullptr || Left == nullptr || Right == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V high multiplication has invalid register operands"); - const Register DestinationRegister = *Destination; - const Register RightRegister = *Right; - auto I64 = LLT::makeInteger(64); - if (!I64) - return std::unexpected(I64.error()); - auto ExtendedLeft = createTemporary(Function, *I64, Target); - auto ExtendedRight = createTemporary(Function, *I64, Target); - auto Product = createTemporary(Function, *I64, Target); - if (!ExtendedLeft || !ExtendedRight || !Product) { - return std::unexpected(!ExtendedLeft - ? ExtendedLeft.error() - : (!ExtendedRight ? ExtendedRight.error() : Product.error())); - } - - auto RemoveRight = Instruction.eraseOperand(2); - if (!RemoveRight) - return std::unexpected(RemoveRight.error()); - MachineIRBuilder Builder(Function, Target); - MachineInstr *Anchor = &Instruction; - if (Signed) { - auto SetDestination = Instruction.operandAt(0)->setRegister(*ExtendedLeft); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto Immediate = appendImmediate(Instruction, 0); - if (!Immediate) - return std::unexpected(Immediate.error()); - auto SetOpcode = Instruction.setOpcode(targetOpcode(InstructionOpcode::ADDIW)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - auto ExtendRight = buildImmediateAfter(Builder, *Anchor, InstructionOpcode::ADDIW, - *ExtendedRight, RightRegister, 0); - if (!ExtendRight) - return std::unexpected(ExtendRight.error()); - Anchor = *ExtendRight; - } else { - auto ShiftedLeft = createTemporary(Function, *I64, Target); - auto ShiftedRight = createTemporary(Function, *I64, Target); - if (!ShiftedLeft || !ShiftedRight) - return std::unexpected(!ShiftedLeft ? ShiftedLeft.error() : ShiftedRight.error()); - auto SetDestination = Instruction.operandAt(0)->setRegister(*ShiftedLeft); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto Immediate = appendImmediate(Instruction, 32); - if (!Immediate) - return std::unexpected(Immediate.error()); - auto SetOpcode = Instruction.setOpcode(targetOpcode(InstructionOpcode::SLLI)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - auto ExtendLeft = buildImmediateAfter(Builder, *Anchor, InstructionOpcode::SRLI, - *ExtendedLeft, *ShiftedLeft, 32); - if (!ExtendLeft) - return std::unexpected(ExtendLeft.error()); - auto ShiftRight = buildImmediateAfter(Builder, **ExtendLeft, InstructionOpcode::SLLI, - *ShiftedRight, RightRegister, 32); - if (!ShiftRight) - return std::unexpected(ShiftRight.error()); - auto ExtendRight = buildImmediateAfter(Builder, **ShiftRight, InstructionOpcode::SRLI, - *ExtendedRight, *ShiftedRight, 32); - if (!ExtendRight) - return std::unexpected(ExtendRight.error()); - Anchor = *ExtendRight; - } - auto Multiply = buildBinaryAfter(Builder, *Anchor, InstructionOpcode::MUL, *Product, - *ExtendedLeft, *ExtendedRight); - if (!Multiply) - return std::unexpected(Multiply.error()); - auto Extract = buildImmediateAfter(Builder, **Multiply, - Signed ? InstructionOpcode::SRAI : InstructionOpcode::SRLI, - DestinationRegister, *Product, 32); - if (!Extract) - return std::unexpected(Extract.error()); - return {}; - } - - [[nodiscard]] static Expected<void> selectIntegerMinMax(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - std::optional<LLT> Type, - GenericOpcode Generic) { - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *LeftOperand = Instruction.operandAt(1); - const MachineOperand *RightOperand = Instruction.operandAt(2); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - if (!Type || Destination == nullptr || Left == nullptr || Right == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V integer min/max has invalid operands"); - auto I1 = LLT::makeInteger(1); - if (!I1) - return std::unexpected(I1.error()); - auto Condition = createTemporary(Function, *I1, Target); - if (!Condition) - return std::unexpected(Condition.error()); - const Register DestinationRegister = *Destination; - const Register LeftRegister = *Left; - const Register RightRegister = *Right; - const bool Unsigned = Generic == GenericOpcode::G_UMIN || Generic == GenericOpcode::G_UMAX; - auto NormalizedLeft = - normalizeIntegerBefore(Instruction, Function, Target, LeftRegister, *Type, !Unsigned); - auto NormalizedRight = - normalizeIntegerBefore(Instruction, Function, Target, RightRegister, *Type, !Unsigned); - if (!NormalizedLeft || !NormalizedRight) - return std::unexpected(!NormalizedLeft ? NormalizedLeft.error() : NormalizedRight.error()); - auto SetCondition = Instruction.operandAt(0)->setRegister(*Condition); - if (!SetCondition) - return std::unexpected(SetCondition.error()); - auto SetLeft = Instruction.operandAt(1)->setRegister(*NormalizedLeft); - if (!SetLeft) - return std::unexpected(SetLeft.error()); - auto SetRight = Instruction.operandAt(2)->setRegister(*NormalizedRight); - if (!SetRight) - return std::unexpected(SetRight.error()); - auto SetCompare = Instruction.setOpcode( - targetOpcode(Unsigned ? InstructionOpcode::SLTU : InstructionOpcode::SLT)); - if (!SetCompare) - return std::unexpected(SetCompare.error()); - const bool IsMaximum = Generic == GenericOpcode::G_SMAX || Generic == GenericOpcode::G_UMAX; - const Register TrueValue = IsMaximum ? RightRegister : LeftRegister; - const Register FalseValue = IsMaximum ? LeftRegister : RightRegister; - MachineIRBuilder Builder(Function, Target); - auto Selected = buildGPRSelectAfter(Builder, Instruction, Function, Target, *Type, - DestinationRegister, *Condition, TrueValue, FalseValue); - if (!Selected) - return std::unexpected(Selected.error()); - return {}; - } - - [[nodiscard]] static Expected<void> selectValue(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - std::optional<LLT> Type) { - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *ConditionOperand = Instruction.operandAt(1); - const MachineOperand *TrueOperand = Instruction.operandAt(2); - const MachineOperand *FalseOperand = Instruction.operandAt(3); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Condition = - ConditionOperand == nullptr ? nullptr : ConditionOperand->getIf<Register>(); - const Register *TrueValue = TrueOperand == nullptr ? nullptr : TrueOperand->getIf<Register>(); - const Register *FalseValue = - FalseOperand == nullptr ? nullptr : FalseOperand->getIf<Register>(); - if (!Type || Destination == nullptr || Condition == nullptr || TrueValue == nullptr || - FalseValue == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V value selection has invalid operands"); - } - const Register DestinationRegister = *Destination; - const Register TrueRegister = *TrueValue; - const Register FalseRegister = *FalseValue; - auto I1 = LLT::makeInteger(1); - if (!I1) - return std::unexpected(I1.error()); - auto NormalizedCondition = createTemporary(Function, *I1, Target); - if (!NormalizedCondition) - return std::unexpected(NormalizedCondition.error()); - auto SetCondition = Instruction.operandAt(0)->setRegister(*NormalizedCondition); - if (!SetCondition) - return std::unexpected(SetCondition.error()); - auto EraseFalse = Instruction.eraseOperand(3); - if (!EraseFalse) - return std::unexpected(EraseFalse.error()); - auto EraseTrue = Instruction.eraseOperand(2); - if (!EraseTrue) - return std::unexpected(EraseTrue.error()); - auto Mask = appendImmediate(Instruction, 1); - if (!Mask) - return std::unexpected(Mask.error()); - auto SetNormalize = Instruction.setOpcode(targetOpcode(InstructionOpcode::ANDI)); - if (!SetNormalize) - return std::unexpected(SetNormalize.error()); - - MachineIRBuilder Builder(Function, Target); - if (!Type->isFloat()) { - auto Selected = - buildGPRSelectAfter(Builder, Instruction, Function, Target, *Type, DestinationRegister, - *NormalizedCondition, TrueRegister, FalseRegister); - if (!Selected) - return std::unexpected(Selected.error()); - return {}; - } - - auto IntegerType = LLT::makeInteger(Type->scalarBitWidth()); - if (!IntegerType) - return std::unexpected(IntegerType.error()); - auto IntegerTrue = createTemporary(Function, *IntegerType, Target); - auto IntegerFalse = createTemporary(Function, *IntegerType, Target); - auto IntegerResult = createTemporary(Function, *IntegerType, Target); - if (!IntegerTrue || !IntegerFalse || !IntegerResult) { - return std::unexpected(!IntegerTrue - ? IntegerTrue.error() - : (!IntegerFalse ? IntegerFalse.error() : IntegerResult.error())); - } - const InstructionOpcode ToInteger = - Type->scalarBitWidth() == 32 ? InstructionOpcode::FMV_X_W : InstructionOpcode::FMV_X_D; - const InstructionOpcode ToFloat = - Type->scalarBitWidth() == 32 ? InstructionOpcode::FMV_W_X : InstructionOpcode::FMV_D_X; - auto MoveTrue = buildUnaryAfter(Builder, Instruction, ToInteger, *IntegerTrue, TrueRegister); - if (!MoveTrue) - return std::unexpected(MoveTrue.error()); - auto MoveFalse = buildUnaryAfter(Builder, **MoveTrue, ToInteger, *IntegerFalse, FalseRegister); - if (!MoveFalse) - return std::unexpected(MoveFalse.error()); - auto Selected = - buildGPRSelectAfter(Builder, **MoveFalse, Function, Target, *IntegerType, *IntegerResult, - *NormalizedCondition, *IntegerTrue, *IntegerFalse); - if (!Selected) - return std::unexpected(Selected.error()); - auto MoveResult = - buildUnaryAfter(Builder, **Selected, ToFloat, DestinationRegister, *IntegerResult); - if (!MoveResult) - return std::unexpected(MoveResult.error()); - return {}; - } - - [[nodiscard]] static Expected<void> selectAbsoluteValue(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - std::optional<LLT> Type) { - 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 (!Type || Destination == nullptr || Source == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V absolute value has invalid operands"); - const Register DestinationRegister = *Destination; - const Register SourceRegister = *Source; - auto NormalizedSource = - normalizeIntegerBefore(Instruction, Function, Target, SourceRegister, *Type, true); - if (!NormalizedSource) - return std::unexpected(NormalizedSource.error()); - auto SignMask = createTemporary(Function, *Type, Target); - auto Flipped = createTemporary(Function, *Type, Target); - if (!SignMask || !Flipped) - return std::unexpected(!SignMask ? SignMask.error() : Flipped.error()); - auto SetMask = Instruction.operandAt(0)->setRegister(*SignMask); - if (!SetMask) - return std::unexpected(SetMask.error()); - auto SetSource = Instruction.operandAt(1)->setRegister(*NormalizedSource); - if (!SetSource) - return std::unexpected(SetSource.error()); - const std::uint32_t ContainerBits = Type->scalarBitWidth() <= 32 ? 32 : 64; - auto Shift = appendImmediate(Instruction, ContainerBits - 1U); - if (!Shift) - return std::unexpected(Shift.error()); - const bool IsWord = Type->scalarBitWidth() <= 32; - auto SetShift = Instruction.setOpcode( - targetOpcode(IsWord ? InstructionOpcode::SRAIW : InstructionOpcode::SRAI)); - if (!SetShift) - return std::unexpected(SetShift.error()); - MachineIRBuilder Builder(Function, Target); - auto Flip = buildBinaryAfter(Builder, Instruction, InstructionOpcode::XOR, *Flipped, - *NormalizedSource, *SignMask); - if (!Flip) - return std::unexpected(Flip.error()); - auto Result = - buildBinaryAfter(Builder, **Flip, IsWord ? InstructionOpcode::SUBW : InstructionOpcode::SUB, - DestinationRegister, *Flipped, *SignMask); - if (!Result) - return std::unexpected(Result.error()); - return {}; - } - - [[nodiscard]] static Expected<std::pair<Register, MachineInstr *>> - buildSignedOverflowAfter(MachineIRBuilder &Builder, MachineInstr &Anchor, - MachineFunction &Function, const TargetDescription &Target, LLT Type, - Register Result, Register Left, Register Right, bool IsAdd) { - auto FirstDifference = createTemporary(Function, Type, Target); - auto SecondDifference = createTemporary(Function, Type, Target); - auto SignBits = createTemporary(Function, Type, Target); - auto I1 = LLT::makeInteger(1); - if (!I1) - return std::unexpected(I1.error()); - auto Overflow = createTemporary(Function, *I1, Target); - if (!FirstDifference || !SecondDifference || !SignBits || !Overflow) { - return std::unexpected(!FirstDifference ? FirstDifference.error() - : !SecondDifference ? SecondDifference.error() - : !SignBits ? SignBits.error() - : Overflow.error()); - } - const Register FirstLeft = IsAdd ? Result : Left; - const Register FirstRight = IsAdd ? Left : Right; - const Register SecondLeft = Result; - const Register SecondRight = IsAdd ? Right : Left; - auto First = buildBinaryAfter(Builder, Anchor, InstructionOpcode::XOR, *FirstDifference, - FirstLeft, FirstRight); - if (!First) - return std::unexpected(First.error()); - auto Second = buildBinaryAfter(Builder, **First, InstructionOpcode::XOR, *SecondDifference, - SecondLeft, SecondRight); - if (!Second) - return std::unexpected(Second.error()); - auto Both = buildBinaryAfter(Builder, **Second, InstructionOpcode::AND, *SignBits, - *FirstDifference, *SecondDifference); - if (!Both) - return std::unexpected(Both.error()); - const InstructionOpcode Shift = - Type.scalarBitWidth() == 32 ? InstructionOpcode::SRLIW : InstructionOpcode::SRLI; - auto Extract = buildImmediateAfter(Builder, **Both, Shift, *Overflow, *SignBits, - Type.scalarBitWidth() - 1U); - if (!Extract) - return std::unexpected(Extract.error()); - return std::pair{*Overflow, *Extract}; - } - - [[nodiscard]] static Expected<void> - selectOverflow(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target, std::optional<LLT> Type, GenericOpcode Generic) { - const MachineOperand *ResultOperand = Instruction.operandAt(0); - const MachineOperand *OverflowOperand = Instruction.operandAt(1); - const MachineOperand *LeftOperand = Instruction.operandAt(2); - const MachineOperand *RightOperand = Instruction.operandAt(3); - const Register *Result = ResultOperand == nullptr ? nullptr : ResultOperand->getIf<Register>(); - const Register *Overflow = - OverflowOperand == nullptr ? nullptr : OverflowOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - if (!Type || Result == nullptr || Overflow == nullptr || Left == nullptr || Right == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V overflow operation has invalid operands"); - } - const Register ResultRegister = *Result; - const Register OverflowRegister = *Overflow; - const Register LeftRegister = *Left; - const Register RightRegister = *Right; - auto RemoveOverflow = Instruction.eraseOperand(1); - if (!RemoveOverflow) - return std::unexpected(RemoveOverflow.error()); - const bool IsWord = Type->scalarBitWidth() <= 32; - const bool IsAdd = Generic == GenericOpcode::G_UADDO || Generic == GenericOpcode::G_SADDO; - const bool IsSub = Generic == GenericOpcode::G_USUBO || Generic == GenericOpcode::G_SSUBO; - const bool IsUnsigned = Generic == GenericOpcode::G_UADDO || - Generic == GenericOpcode::G_USUBO || Generic == GenericOpcode::G_UMULO; - const InstructionOpcode Arithmetic = - IsAdd ? (IsWord ? InstructionOpcode::ADDW : InstructionOpcode::ADD) - : IsSub ? (IsWord ? InstructionOpcode::SUBW : InstructionOpcode::SUB) - : (IsWord ? InstructionOpcode::MULW : InstructionOpcode::MUL); - auto SetArithmetic = Instruction.setOpcode(targetOpcode(Arithmetic)); - if (!SetArithmetic) - return std::unexpected(SetArithmetic.error()); - MachineIRBuilder Builder(Function, Target); - if (IsUnsigned && IsAdd) { - auto Carry = buildBinaryAfter(Builder, Instruction, InstructionOpcode::SLTU, OverflowRegister, - ResultRegister, LeftRegister); - if (!Carry) - return std::unexpected(Carry.error()); - return {}; - } - if (IsUnsigned && IsSub) { - auto Borrow = buildBinaryAfter(Builder, Instruction, InstructionOpcode::SLTU, - OverflowRegister, LeftRegister, RightRegister); - if (!Borrow) - return std::unexpected(Borrow.error()); - return {}; - } - if (IsAdd || IsSub) { - auto FirstDifference = createTemporary(Function, *Type, Target); - auto SecondDifference = createTemporary(Function, *Type, Target); - auto SignBits = createTemporary(Function, *Type, Target); - if (!FirstDifference || !SecondDifference || !SignBits) { - return std::unexpected( - !FirstDifference ? FirstDifference.error() - : (!SecondDifference ? SecondDifference.error() : SignBits.error())); - } - const Register FirstLeft = IsAdd ? ResultRegister : LeftRegister; - const Register FirstRight = IsAdd ? LeftRegister : RightRegister; - const Register SecondLeft = ResultRegister; - const Register SecondRight = IsAdd ? RightRegister : LeftRegister; - auto First = buildBinaryAfter(Builder, Instruction, InstructionOpcode::XOR, *FirstDifference, - FirstLeft, FirstRight); - if (!First) - return std::unexpected(First.error()); - auto Second = buildBinaryAfter(Builder, **First, InstructionOpcode::XOR, *SecondDifference, - SecondLeft, SecondRight); - if (!Second) - return std::unexpected(Second.error()); - auto Both = buildBinaryAfter(Builder, **Second, InstructionOpcode::AND, *SignBits, - *FirstDifference, *SecondDifference); - if (!Both) - return std::unexpected(Both.error()); - auto OverflowResult = buildImmediateAfter( - Builder, **Both, IsWord ? InstructionOpcode::SRLIW : InstructionOpcode::SRLI, - OverflowRegister, *SignBits, Type->scalarBitWidth() - 1U); - if (!OverflowResult) - return std::unexpected(OverflowResult.error()); - return {}; - } - - auto I64 = LLT::makeInteger(64); - if (!I64) - return std::unexpected(I64.error()); - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - if (!IsWord) { - auto High = createTemporary(Function, *I64, Target); - if (!High) - return std::unexpected(High.error()); - auto HighProduct = buildBinaryAfter( - Builder, Instruction, IsUnsigned ? InstructionOpcode::MULHU : InstructionOpcode::MULH, - *High, LeftRegister, RightRegister); - if (!HighProduct) - return std::unexpected(HighProduct.error()); - if (IsUnsigned) { - auto OverflowResult = - buildBinaryAfter(Builder, **HighProduct, InstructionOpcode::SLTU, OverflowRegister, - Register::fromPhysical(*Zero), *High); - if (!OverflowResult) - return std::unexpected(OverflowResult.error()); - return {}; - } - auto ExpectedHigh = createTemporary(Function, *I64, Target); - auto Difference = createTemporary(Function, *I64, Target); - if (!ExpectedHigh || !Difference) - return std::unexpected(!ExpectedHigh ? ExpectedHigh.error() : Difference.error()); - auto Sign = buildImmediateAfter(Builder, **HighProduct, InstructionOpcode::SRAI, - *ExpectedHigh, ResultRegister, 63); - if (!Sign) - return std::unexpected(Sign.error()); - auto Compare = buildBinaryAfter(Builder, **Sign, InstructionOpcode::XOR, *Difference, *High, - *ExpectedHigh); - if (!Compare) - return std::unexpected(Compare.error()); - auto OverflowResult = - buildBinaryAfter(Builder, **Compare, InstructionOpcode::SLTU, OverflowRegister, - Register::fromPhysical(*Zero), *Difference); - if (!OverflowResult) - return std::unexpected(OverflowResult.error()); - return {}; - } - - auto ExtendedLeft = createTemporary(Function, *I64, Target); - auto ExtendedRight = createTemporary(Function, *I64, Target); - auto FullProduct = createTemporary(Function, *I64, Target); - if (!ExtendedLeft || !ExtendedRight || !FullProduct) { - return std::unexpected(!ExtendedLeft - ? ExtendedLeft.error() - : (!ExtendedRight ? ExtendedRight.error() : FullProduct.error())); - } - MachineInstr *Anchor = &Instruction; - if (IsUnsigned) { - auto ShiftedLeft = createTemporary(Function, *I64, Target); - auto ShiftedRight = createTemporary(Function, *I64, Target); - if (!ShiftedLeft || !ShiftedRight) - return std::unexpected(!ShiftedLeft ? ShiftedLeft.error() : ShiftedRight.error()); - auto LeftShift = buildImmediateAfter(Builder, *Anchor, InstructionOpcode::SLLI, *ShiftedLeft, - LeftRegister, 32); - if (!LeftShift) - return std::unexpected(LeftShift.error()); - auto LeftExtend = buildImmediateAfter(Builder, **LeftShift, InstructionOpcode::SRLI, - *ExtendedLeft, *ShiftedLeft, 32); - if (!LeftExtend) - return std::unexpected(LeftExtend.error()); - auto RightShift = buildImmediateAfter(Builder, **LeftExtend, InstructionOpcode::SLLI, - *ShiftedRight, RightRegister, 32); - if (!RightShift) - return std::unexpected(RightShift.error()); - auto RightExtend = buildImmediateAfter(Builder, **RightShift, InstructionOpcode::SRLI, - *ExtendedRight, *ShiftedRight, 32); - if (!RightExtend) - return std::unexpected(RightExtend.error()); - Anchor = *RightExtend; - } else { - auto LeftExtend = buildImmediateAfter(Builder, *Anchor, InstructionOpcode::ADDIW, - *ExtendedLeft, LeftRegister, 0); - if (!LeftExtend) - return std::unexpected(LeftExtend.error()); - auto RightExtend = buildImmediateAfter(Builder, **LeftExtend, InstructionOpcode::ADDIW, - *ExtendedRight, RightRegister, 0); - if (!RightExtend) - return std::unexpected(RightExtend.error()); - Anchor = *RightExtend; - } - auto Product = buildBinaryAfter(Builder, *Anchor, InstructionOpcode::MUL, *FullProduct, - *ExtendedLeft, *ExtendedRight); - if (!Product) - return std::unexpected(Product.error()); - if (IsUnsigned) { - auto High = createTemporary(Function, *I64, Target); - if (!High) - return std::unexpected(High.error()); - auto ExtractHigh = - buildImmediateAfter(Builder, **Product, InstructionOpcode::SRLI, *High, *FullProduct, 32); - if (!ExtractHigh) - return std::unexpected(ExtractHigh.error()); - auto OverflowResult = - buildBinaryAfter(Builder, **ExtractHigh, InstructionOpcode::SLTU, OverflowRegister, - Register::fromPhysical(*Zero), *High); - if (!OverflowResult) - return std::unexpected(OverflowResult.error()); - return {}; - } - auto Difference = createTemporary(Function, *I64, Target); - if (!Difference) - return std::unexpected(Difference.error()); - auto Compare = buildBinaryAfter(Builder, **Product, InstructionOpcode::XOR, *Difference, - *FullProduct, ResultRegister); - if (!Compare) - return std::unexpected(Compare.error()); - auto OverflowResult = - buildBinaryAfter(Builder, **Compare, InstructionOpcode::SLTU, OverflowRegister, - Register::fromPhysical(*Zero), *Difference); - if (!OverflowResult) - return std::unexpected(OverflowResult.error()); - return {}; - } - - [[nodiscard]] static Expected<void> selectExtendedOverflow(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - std::optional<LLT> Type, - GenericOpcode Generic) { - const MachineOperand *ResultOperand = Instruction.operandAt(0); - const MachineOperand *CarryOutOperand = Instruction.operandAt(1); - const MachineOperand *LeftOperand = Instruction.operandAt(2); - const MachineOperand *RightOperand = Instruction.operandAt(3); - const MachineOperand *CarryInOperand = Instruction.operandAt(4); - const Register *Result = ResultOperand == nullptr ? nullptr : ResultOperand->getIf<Register>(); - const Register *CarryOut = - CarryOutOperand == nullptr ? nullptr : CarryOutOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - const Register *CarryIn = - CarryInOperand == nullptr ? nullptr : CarryInOperand->getIf<Register>(); - if (!Type || Result == nullptr || CarryOut == nullptr || Left == nullptr || Right == nullptr || - CarryIn == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V extended overflow operation has invalid operands"); - } - const Register ResultRegister = *Result; - const Register CarryOutRegister = *CarryOut; - const Register LeftRegister = *Left; - const Register RightRegister = *Right; - const Register CarryInRegister = *CarryIn; - const bool IsAdd = Generic == GenericOpcode::G_UADDE || Generic == GenericOpcode::G_SADDE; - const bool IsUnsigned = Generic == GenericOpcode::G_UADDE || Generic == GenericOpcode::G_USUBE; - const bool IsWord = Type->scalarBitWidth() <= 32; - const InstructionOpcode Arithmetic = - IsAdd ? (IsWord ? InstructionOpcode::ADDW : InstructionOpcode::ADD) - : (IsWord ? InstructionOpcode::SUBW : InstructionOpcode::SUB); - auto Partial = createTemporary(Function, *Type, Target); - auto I1 = LLT::makeInteger(1); - if (!Partial || !I1) - return std::unexpected(!Partial ? Partial.error() : I1.error()); - auto NormalizedCarry = createTemporary(Function, *I1, Target); - if (!NormalizedCarry) - return std::unexpected(NormalizedCarry.error()); - auto SetPartial = Instruction.operandAt(0)->setRegister(*Partial); - if (!SetPartial) - return std::unexpected(SetPartial.error()); - auto RemoveCarryOut = Instruction.eraseOperand(1); - if (!RemoveCarryOut) - return std::unexpected(RemoveCarryOut.error()); - auto RemoveCarryIn = Instruction.eraseOperand(3); - if (!RemoveCarryIn) - return std::unexpected(RemoveCarryIn.error()); - auto SetArithmetic = Instruction.setOpcode(targetOpcode(Arithmetic)); - if (!SetArithmetic) - return std::unexpected(SetArithmetic.error()); - MachineIRBuilder Builder(Function, Target); - auto Normalize = buildImmediateAfter(Builder, Instruction, InstructionOpcode::ANDI, - *NormalizedCarry, CarryInRegister, 1); - if (!Normalize) - return std::unexpected(Normalize.error()); - auto Final = buildBinaryAfter(Builder, **Normalize, Arithmetic, ResultRegister, *Partial, - *NormalizedCarry); - if (!Final) - return std::unexpected(Final.error()); - - if (IsUnsigned) { - auto FirstCarry = createTemporary(Function, *I1, Target); - auto SecondCarry = createTemporary(Function, *I1, Target); - if (!FirstCarry || !SecondCarry) - return std::unexpected(!FirstCarry ? FirstCarry.error() : SecondCarry.error()); - auto First = - buildBinaryAfter(Builder, **Final, InstructionOpcode::SLTU, *FirstCarry, - IsAdd ? *Partial : LeftRegister, IsAdd ? LeftRegister : RightRegister); - if (!First) - return std::unexpected(First.error()); - auto Second = - buildBinaryAfter(Builder, **First, InstructionOpcode::SLTU, *SecondCarry, - IsAdd ? ResultRegister : *Partial, IsAdd ? *Partial : *NormalizedCarry); - if (!Second) - return std::unexpected(Second.error()); - auto Combined = buildBinaryAfter(Builder, **Second, InstructionOpcode::OR, CarryOutRegister, - *FirstCarry, *SecondCarry); - if (!Combined) - return std::unexpected(Combined.error()); - return {}; - } - - auto FirstOverflow = buildSignedOverflowAfter(Builder, **Final, Function, Target, *Type, - *Partial, LeftRegister, RightRegister, IsAdd); - if (!FirstOverflow) - return std::unexpected(FirstOverflow.error()); - auto SecondOverflow = - buildSignedOverflowAfter(Builder, *FirstOverflow->second, Function, Target, *Type, - ResultRegister, *Partial, *NormalizedCarry, IsAdd); - if (!SecondOverflow) - return std::unexpected(SecondOverflow.error()); - auto Combined = buildBinaryAfter(Builder, *SecondOverflow->second, InstructionOpcode::OR, - CarryOutRegister, FirstOverflow->first, SecondOverflow->first); - if (!Combined) - return std::unexpected(Combined.error()); - return {}; - } - - [[nodiscard]] static Expected<void> - selectAtomicRMW(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target, std::optional<LLT> Type, GenericOpcode Generic) { - const MachineOperand *AddressOperand = Instruction.operandAt(1); - const MachineOperand *ValueOperand = Instruction.operandAt(2); - const Register *Address = - AddressOperand == nullptr ? nullptr : AddressOperand->getIf<Register>(); - const Register *Value = ValueOperand == nullptr ? nullptr : ValueOperand->getIf<Register>(); - if (!Type || Address == nullptr || Value == nullptr) - return makeUnexpected(ErrorCode::VerificationError, "RISC-V atomic RMW has invalid operands"); - Register SelectedValue = *Value; - const bool IsWord = Type->scalarBitWidth() == 32; - if (Generic == GenericOpcode::G_ATOMICRMW_SUB) { - auto Negated = createTemporary(Function, *Type, Target); - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Negated || !Zero) - return std::unexpected(!Negated ? Negated.error() : Zero.error()); - MachineIRBuilder Builder(Function, Target); - auto Point = Builder.setInsertPointBefore(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto Pending = Builder.buildInstr( - targetOpcode(IsWord ? InstructionOpcode::SUBW : InstructionOpcode::SUB)); - if (!Pending) - return std::unexpected(Pending.error()); - auto Def = Pending->addDef(*Negated); - if (!Def) - return std::unexpected(Def.error()); - auto Left = Pending->addUse(Register::fromPhysical(*Zero)); - if (!Left) - return std::unexpected(Left.error()); - auto Right = Pending->addUse(*Value); - if (!Right) - return std::unexpected(Right.error()); - auto Built = Pending->build(); - if (!Built) - return std::unexpected(Built.error()); - SelectedValue = *Negated; - } - const auto SelectOpcode = [Generic, IsWord]() -> InstructionOpcode { - switch (Generic) { - case GenericOpcode::G_ATOMICRMW_XCHG: - return IsWord ? InstructionOpcode::AMOSWAP_W : InstructionOpcode::AMOSWAP_D; - case GenericOpcode::G_ATOMICRMW_ADD: - case GenericOpcode::G_ATOMICRMW_SUB: - return IsWord ? InstructionOpcode::AMOADD_W : InstructionOpcode::AMOADD_D; - case GenericOpcode::G_ATOMICRMW_AND: - return IsWord ? InstructionOpcode::AMOAND_W : InstructionOpcode::AMOAND_D; - case GenericOpcode::G_ATOMICRMW_OR: - return IsWord ? InstructionOpcode::AMOOR_W : InstructionOpcode::AMOOR_D; - case GenericOpcode::G_ATOMICRMW_XOR: - return IsWord ? InstructionOpcode::AMOXOR_W : InstructionOpcode::AMOXOR_D; - case GenericOpcode::G_ATOMICRMW_MAX: - return IsWord ? InstructionOpcode::AMOMAX_W : InstructionOpcode::AMOMAX_D; - case GenericOpcode::G_ATOMICRMW_MIN: - return IsWord ? InstructionOpcode::AMOMIN_W : InstructionOpcode::AMOMIN_D; - case GenericOpcode::G_ATOMICRMW_UMAX: - return IsWord ? InstructionOpcode::AMOMAXU_W : InstructionOpcode::AMOMAXU_D; - case GenericOpcode::G_ATOMICRMW_UMIN: - return IsWord ? InstructionOpcode::AMOMINU_W : InstructionOpcode::AMOMINU_D; - default: - return InstructionOpcode::Invalid; - } - }; - const InstructionOpcode Selected = SelectOpcode(); - if (Selected == InstructionOpcode::Invalid) - return makeUnexpected(ErrorCode::InternalError, - "RISC-V atomic RMW selection reached an unhandled opcode"); - auto SetValue = Instruction.operandAt(1)->setRegister(SelectedValue); - if (!SetValue) - return std::unexpected(SetValue.error()); - auto SetAddress = Instruction.operandAt(2)->setRegister(*Address); - if (!SetAddress) - return std::unexpected(SetAddress.error()); - return Instruction.setOpcode(targetOpcode(Selected)); - } - - [[nodiscard]] static Expected<void> selectIntegerExtension(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - GenericOpcode Generic, - std::optional<LLT> Type) { - const std::optional<LLT> SourceType = operandType(Function, Instruction, 1); - 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 (!Type || !SourceType || Destination == nullptr || Source == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V integer extension has invalid register operands"); - const Register DestinationRegister = *Destination; - if (Generic == GenericOpcode::G_ANYEXT) - return Instruction.setOpcode(opcode(GenericOpcode::COPY)); - - const std::uint32_t DestinationBits = Type->scalarBitWidth(); - const std::uint32_t SourceBits = SourceType->scalarBitWidth(); - if (Generic == GenericOpcode::G_SEXT && DestinationBits == 64 && SourceBits == 32) { - auto ImmediateResult = appendImmediate(Instruction, 0); - if (!ImmediateResult) - return std::unexpected(ImmediateResult.error()); - return Instruction.setOpcode(targetOpcode(InstructionOpcode::ADDIW)); - } - - const std::uint32_t ContainerBits = DestinationBits <= 32 ? 32 : 64; - const std::int64_t Shift = static_cast<std::int64_t>(ContainerBits - SourceBits); - const InstructionOpcode ShiftLeft = - ContainerBits == 32 ? InstructionOpcode::SLLIW : InstructionOpcode::SLLI; - const InstructionOpcode ShiftRight = - Generic == GenericOpcode::G_SEXT - ? (ContainerBits == 32 ? InstructionOpcode::SRAIW : InstructionOpcode::SRAI) - : (ContainerBits == 32 ? InstructionOpcode::SRLIW : InstructionOpcode::SRLI); - auto Temporary = createTemporary(Function, *Type, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto SetDestination = Instruction.operandAt(0)->setRegister(*Temporary); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto SetOpcode = Instruction.setOpcode(targetOpcode(ShiftLeft)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - auto ShiftImmediate = appendImmediate(Instruction, Shift); - if (!ShiftImmediate) - return std::unexpected(ShiftImmediate.error()); - - MachineIRBuilder Builder(Function, Target); - auto Shifted = buildImmediateAfter(Builder, Instruction, ShiftRight, DestinationRegister, - *Temporary, Shift); - if (!Shifted) - return std::unexpected(Shifted.error()); - return {}; - } - - [[nodiscard]] static Expected<void> selectSignExtendInRegister(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - std::optional<LLT> Type) { - 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>(); - std::optional<std::int64_t> Width; - if (const MachineOperand *WidthOperand = Instruction.operandAt(2); WidthOperand != nullptr) { - auto ParsedWidth = signedImmediate(*WidthOperand); - if (ParsedWidth) - Width = *ParsedWidth; - } - if (!Type || Destination == nullptr || Source == nullptr || !Width || *Width <= 0 || - static_cast<std::uint64_t>(*Width) > Type->scalarBitWidth()) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V sign-extension-in-register has invalid operands"); - const Register DestinationRegister = *Destination; - if (static_cast<std::uint64_t>(*Width) == Type->scalarBitWidth()) { - auto EraseWidth = Instruction.eraseOperand(2); - if (!EraseWidth) - return std::unexpected(EraseWidth.error()); - return Instruction.setOpcode(opcode(GenericOpcode::COPY)); - } - - const std::uint32_t TypeBits = Type->scalarBitWidth(); - if (TypeBits == 64 && *Width == 32) { - auto ImmediateResult = replaceImmediate(Instruction, 2, 0); - if (!ImmediateResult) - return std::unexpected(ImmediateResult.error()); - return Instruction.setOpcode(targetOpcode(InstructionOpcode::ADDIW)); - } - const std::uint32_t ContainerBits = TypeBits <= 32 ? 32 : 64; - const std::int64_t Shift = static_cast<std::int64_t>(ContainerBits) - *Width; - const InstructionOpcode ShiftLeft = - ContainerBits == 32 ? InstructionOpcode::SLLIW : InstructionOpcode::SLLI; - const InstructionOpcode ShiftRight = - ContainerBits == 32 ? InstructionOpcode::SRAIW : InstructionOpcode::SRAI; - auto Temporary = createTemporary(Function, *Type, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto SetDestination = Instruction.operandAt(0)->setRegister(*Temporary); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto SetOpcode = Instruction.setOpcode(targetOpcode(ShiftLeft)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - auto ShiftImmediate = replaceImmediate(Instruction, 2, Shift); - if (!ShiftImmediate) - return std::unexpected(ShiftImmediate.error()); - - MachineIRBuilder Builder(Function, Target); - auto Shifted = buildImmediateAfter(Builder, Instruction, ShiftRight, DestinationRegister, - *Temporary, Shift); - if (!Shifted) - return std::unexpected(Shifted.error()); - return {}; - } - - [[nodiscard]] static Expected<void> selectIntegerCompare(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const MachineOperand *PredicateMO = Instruction.operandAt(1); - const auto *Predicate = - PredicateMO == nullptr ? nullptr : PredicateMO->getIf<PredicateOperand>(); - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *LeftOperand = Instruction.operandAt(2); - const MachineOperand *RightOperand = Instruction.operandAt(3); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - const std::optional<LLT> LeftType = operandType(Function, Instruction, 2); - if (Predicate == nullptr || Predicate->integerPredicate() == nullptr || - Destination == nullptr || Left == nullptr || Right == nullptr || !LeftType) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V integer comparison has invalid operands"); - const IntegerPredicate Condition = *Predicate->integerPredicate(); - const Register DestinationRegister = *Destination; - const bool SignedOperands = Condition == IntegerPredicate::SignedGreaterThan || - Condition == IntegerPredicate::SignedGreaterEqual || - Condition == IntegerPredicate::SignedLessThan || - Condition == IntegerPredicate::SignedLessEqual; - auto LeftRegister = - normalizeIntegerBefore(Instruction, Function, Target, *Left, *LeftType, SignedOperands); - auto RightRegister = - normalizeIntegerBefore(Instruction, Function, Target, *Right, *LeftType, SignedOperands); - if (!LeftRegister || !RightRegister) - return std::unexpected(!LeftRegister ? LeftRegister.error() : RightRegister.error()); - auto ErasePredicate = Instruction.eraseOperand(1); - if (!ErasePredicate) - return std::unexpected(ErasePredicate.error()); - - const auto SetSources = [&Instruction](Register NewLeft, Register NewRight) -> Expected<void> { - auto SetLeft = Instruction.operandAt(1)->setRegister(NewLeft); - if (!SetLeft) - return std::unexpected(SetLeft.error()); - return Instruction.operandAt(2)->setRegister(NewRight); - }; - const auto StrictOpcode = [](IntegerPredicate Value) -> std::optional<InstructionOpcode> { - switch (Value) { - case IntegerPredicate::SignedLessThan: - return InstructionOpcode::SLT; - case IntegerPredicate::UnsignedLessThan: - return InstructionOpcode::SLTU; - default: - return std::nullopt; - } - }; - - IntegerPredicate StrictCondition = Condition; - bool Swap = false; - bool Invert = false; - switch (Condition) { - case IntegerPredicate::Equal: - case IntegerPredicate::NotEqual: - break; - case IntegerPredicate::SignedGreaterThan: - StrictCondition = IntegerPredicate::SignedLessThan; - Swap = true; - break; - case IntegerPredicate::SignedGreaterEqual: - StrictCondition = IntegerPredicate::SignedLessThan; - Invert = true; - break; - case IntegerPredicate::SignedLessThan: - StrictCondition = IntegerPredicate::SignedLessThan; - break; - case IntegerPredicate::SignedLessEqual: - StrictCondition = IntegerPredicate::SignedLessThan; - Swap = true; - Invert = true; - break; - case IntegerPredicate::UnsignedGreaterThan: - StrictCondition = IntegerPredicate::UnsignedLessThan; - Swap = true; - break; - case IntegerPredicate::UnsignedGreaterEqual: - StrictCondition = IntegerPredicate::UnsignedLessThan; - Invert = true; - break; - case IntegerPredicate::UnsignedLessThan: - StrictCondition = IntegerPredicate::UnsignedLessThan; - break; - case IntegerPredicate::UnsignedLessEqual: - StrictCondition = IntegerPredicate::UnsignedLessThan; - Swap = true; - Invert = true; - break; - case IntegerPredicate::Count: - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V integer comparison predicate is invalid"); - } - - MachineIRBuilder Builder(Function, Target); - if (Condition == IntegerPredicate::Equal || Condition == IntegerPredicate::NotEqual) { - auto Temporary = createTemporary(Function, *LeftType, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto SetDestination = Instruction.operandAt(0)->setRegister(*Temporary); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto Set = SetSources(*LeftRegister, *RightRegister); - if (!Set) - return std::unexpected(Set.error()); - auto SetOpcode = Instruction.setOpcode(targetOpcode(InstructionOpcode::XOR)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - if (Condition == IntegerPredicate::Equal) { - auto Result = buildImmediateAfter(Builder, Instruction, InstructionOpcode::SLTIU, - DestinationRegister, *Temporary, 1); - if (!Result) - return std::unexpected(Result.error()); - } else { - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto Result = - buildBinaryAfter(Builder, Instruction, InstructionOpcode::SLTU, DestinationRegister, - Register::fromPhysical(*Zero), *Temporary); - if (!Result) - return std::unexpected(Result.error()); - } - return {}; - } - - const std::optional<InstructionOpcode> OpcodeValue = StrictOpcode(StrictCondition); - if (!OpcodeValue) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V integer comparison predicate is invalid"); - if (Invert) { - auto Temporary = createTemporary(Function, *LeftType, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto SetDestination = Instruction.operandAt(0)->setRegister(*Temporary); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - const Register Base = *Temporary; - if (Swap) { - auto Set = SetSources(*RightRegister, *LeftRegister); - if (!Set) - return std::unexpected(Set.error()); - } else { - auto Set = SetSources(*LeftRegister, *RightRegister); - if (!Set) - return std::unexpected(Set.error()); - } - auto SetOpcode = Instruction.setOpcode(targetOpcode(*OpcodeValue)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - auto Result = buildImmediateAfter(Builder, Instruction, InstructionOpcode::XORI, - DestinationRegister, Base, 1); - if (!Result) - return std::unexpected(Result.error()); - return {}; - } - if (Swap) { - auto Set = SetSources(*RightRegister, *LeftRegister); - if (!Set) - return std::unexpected(Set.error()); - } else { - auto Set = SetSources(*LeftRegister, *RightRegister); - if (!Set) - return std::unexpected(Set.error()); - } - return Instruction.setOpcode(targetOpcode(*OpcodeValue)); - } - - [[nodiscard]] static Expected<void> selectFloatingCompare(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const MachineOperand *PredicateMO = Instruction.operandAt(1); - const auto *Predicate = - PredicateMO == nullptr ? nullptr : PredicateMO->getIf<PredicateOperand>(); - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const MachineOperand *LeftOperand = Instruction.operandAt(2); - const MachineOperand *RightOperand = Instruction.operandAt(3); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - const std::optional<LLT> FloatType = operandType(Function, Instruction, 2); - const std::optional<LLT> ResultType = resultType(Function, Instruction); - if (Predicate == nullptr || Predicate->floatingPredicate() == nullptr || - Destination == nullptr || Left == nullptr || Right == nullptr || !FloatType || !ResultType) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V floating comparison has invalid operands"); - const FloatingPredicate Condition = *Predicate->floatingPredicate(); - const Register DestinationRegister = *Destination; - const Register LeftRegister = *Left; - const Register RightRegister = *Right; - const InstructionOpcode EqualOpcode = - FloatType->scalarBitWidth() == 32 ? InstructionOpcode::FEQ_S : InstructionOpcode::FEQ_D; - const InstructionOpcode LessOpcode = - FloatType->scalarBitWidth() == 32 ? InstructionOpcode::FLT_S : InstructionOpcode::FLT_D; - const InstructionOpcode LessEqualOpcode = - FloatType->scalarBitWidth() == 32 ? InstructionOpcode::FLE_S : InstructionOpcode::FLE_D; - - auto ErasePredicate = Instruction.eraseOperand(1); - if (!ErasePredicate) - return std::unexpected(ErasePredicate.error()); - const auto SetBase = [&Instruction](InstructionOpcode OpcodeValue, Register Def, - Register NewLeft, Register NewRight) -> Expected<void> { - auto SetDestination = Instruction.operandAt(0)->setRegister(Def); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto SetLeft = Instruction.operandAt(1)->setRegister(NewLeft); - if (!SetLeft) - return std::unexpected(SetLeft.error()); - auto SetRight = Instruction.operandAt(2)->setRegister(NewRight); - if (!SetRight) - return std::unexpected(SetRight.error()); - return Instruction.setOpcode(targetOpcode(OpcodeValue)); - }; - const auto BuildInverted = [&](InstructionOpcode BaseOpcode, Register BaseLeft, - Register BaseRight) -> Expected<void> { - auto Temporary = createTemporary(Function, *ResultType, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto Base = SetBase(BaseOpcode, *Temporary, BaseLeft, BaseRight); - if (!Base) - return std::unexpected(Base.error()); - MachineIRBuilder Builder(Function, Target); - auto Invert = buildImmediateAfter(Builder, Instruction, InstructionOpcode::XORI, - DestinationRegister, *Temporary, 1); - if (!Invert) - return std::unexpected(Invert.error()); - return {}; - }; - const auto BuildSelfAnd = [&](bool Invert) -> Expected<void> { - auto First = createTemporary(Function, *ResultType, Target); - auto Second = createTemporary(Function, *ResultType, Target); - if (!First || !Second) - return std::unexpected((!First ? First.error() : Second.error())); - auto Base = SetBase(EqualOpcode, *First, LeftRegister, LeftRegister); - if (!Base) - return std::unexpected(Base.error()); - MachineIRBuilder Builder(Function, Target); - auto SecondInstruction = buildBinaryAfter(Builder, Instruction, EqualOpcode, *Second, - RightRegister, RightRegister); - if (!SecondInstruction) - return std::unexpected(SecondInstruction.error()); - Register CombinedDestination = DestinationRegister; - if (Invert) { - auto Temporary = createTemporary(Function, *ResultType, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - CombinedDestination = *Temporary; - } - auto Combined = buildBinaryAfter(Builder, **SecondInstruction, InstructionOpcode::AND, - CombinedDestination, *First, *Second); - if (!Combined) - return std::unexpected(Combined.error()); - if (!Invert) - return {}; - auto Inverted = buildImmediateAfter(Builder, **Combined, InstructionOpcode::XORI, - DestinationRegister, CombinedDestination, 1); - if (!Inverted) - return std::unexpected(Inverted.error()); - return {}; - }; - const auto BuildOrderedNotEqual = [&](bool Invert) -> Expected<void> { - auto First = createTemporary(Function, *ResultType, Target); - auto Second = createTemporary(Function, *ResultType, Target); - if (!First || !Second) - return std::unexpected((!First ? First.error() : Second.error())); - auto Base = SetBase(LessOpcode, *First, LeftRegister, RightRegister); - if (!Base) - return std::unexpected(Base.error()); - MachineIRBuilder Builder(Function, Target); - auto SecondInstruction = - buildBinaryAfter(Builder, Instruction, LessOpcode, *Second, RightRegister, LeftRegister); - if (!SecondInstruction) - return std::unexpected(SecondInstruction.error()); - Register CombinedDestination = DestinationRegister; - if (Invert) { - auto Temporary = createTemporary(Function, *ResultType, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - CombinedDestination = *Temporary; - } - auto Combined = buildBinaryAfter(Builder, **SecondInstruction, InstructionOpcode::OR, - CombinedDestination, *First, *Second); - if (!Combined) - return std::unexpected(Combined.error()); - if (!Invert) - return {}; - auto Inverted = buildImmediateAfter(Builder, **Combined, InstructionOpcode::XORI, - DestinationRegister, CombinedDestination, 1); - if (!Inverted) - return std::unexpected(Inverted.error()); - return {}; - }; - - switch (Condition) { - case FloatingPredicate::AlwaysFalse: - case FloatingPredicate::AlwaysTrue: { - auto EraseRight = Instruction.eraseOperand(2); - if (!EraseRight) - return std::unexpected(EraseRight.error()); - auto EraseLeft = Instruction.eraseOperand(1); - if (!EraseLeft) - return std::unexpected(EraseLeft.error()); - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto InsertZero = insertPhysicalRegister(Instruction, 1, *Zero, RegisterOperandFlag::Use); - if (!InsertZero) - return std::unexpected(InsertZero.error()); - auto Immediate = - appendImmediate(Instruction, Condition == FloatingPredicate::AlwaysTrue ? 1 : 0); - if (!Immediate) - return std::unexpected(Immediate.error()); - return Instruction.setOpcode(targetOpcode(InstructionOpcode::ADDI)); - } - case FloatingPredicate::OrderedEqual: - return SetBase(EqualOpcode, DestinationRegister, LeftRegister, RightRegister); - case FloatingPredicate::OrderedGreaterThan: - return SetBase(LessOpcode, DestinationRegister, RightRegister, LeftRegister); - case FloatingPredicate::OrderedGreaterEqual: - return SetBase(LessEqualOpcode, DestinationRegister, RightRegister, LeftRegister); - case FloatingPredicate::OrderedLessThan: - return SetBase(LessOpcode, DestinationRegister, LeftRegister, RightRegister); - case FloatingPredicate::OrderedLessEqual: - return SetBase(LessEqualOpcode, DestinationRegister, LeftRegister, RightRegister); - case FloatingPredicate::OrderedNotEqual: - return BuildOrderedNotEqual(false); - case FloatingPredicate::Ordered: - return BuildSelfAnd(false); - case FloatingPredicate::Unordered: - return BuildSelfAnd(true); - case FloatingPredicate::UnorderedEqual: - return BuildOrderedNotEqual(true); - case FloatingPredicate::UnorderedGreaterThan: - return BuildInverted(LessEqualOpcode, LeftRegister, RightRegister); - case FloatingPredicate::UnorderedGreaterEqual: - return BuildInverted(LessOpcode, LeftRegister, RightRegister); - case FloatingPredicate::UnorderedLessThan: - return BuildInverted(LessEqualOpcode, RightRegister, LeftRegister); - case FloatingPredicate::UnorderedLessEqual: - return BuildInverted(LessOpcode, RightRegister, LeftRegister); - case FloatingPredicate::UnorderedNotEqual: - return BuildInverted(EqualOpcode, LeftRegister, RightRegister); - case FloatingPredicate::Count: - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V floating comparison predicate is invalid"); - } - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V floating comparison predicate is invalid"); - } - - [[nodiscard]] static Expected<void> selectFloatToInteger(MachineInstr &Instruction, - MachineFunction &Function, - std::optional<LLT> Type, bool Unsigned) { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - if (!Type || !Source) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V floating-to-integer conversion has invalid types"); - auto Rounding = appendImmediate(Instruction, 1); - if (!Rounding) - return std::unexpected(Rounding.error()); - const bool IsWord = Type->scalarBitWidth() <= 32; - if (Source->scalarBitWidth() == 32) { - return Instruction.setOpcode(targetOpcode( - IsWord ? (Unsigned ? InstructionOpcode::FCVT_WU_S : InstructionOpcode::FCVT_W_S) - : (Unsigned ? InstructionOpcode::FCVT_LU_S : InstructionOpcode::FCVT_L_S))); - } - return Instruction.setOpcode(targetOpcode( - IsWord ? (Unsigned ? InstructionOpcode::FCVT_WU_D : InstructionOpcode::FCVT_W_D) - : (Unsigned ? InstructionOpcode::FCVT_LU_D : InstructionOpcode::FCVT_L_D))); - } - - [[nodiscard]] static Expected<void> selectIntegerToFloat(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - std::optional<LLT> Type, bool Unsigned) { - const std::optional<LLT> Source = operandType(Function, Instruction, 1); - const MachineOperand *SourceOperand = Instruction.operandAt(1); - const Register *SourceRegister = - SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>(); - if (!Type || !Source || SourceRegister == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V integer-to-floating conversion has invalid types"); - auto Normalized = - normalizeIntegerBefore(Instruction, Function, Target, *SourceRegister, *Source, !Unsigned); - if (!Normalized) - return std::unexpected(Normalized.error()); - auto SetSource = Instruction.operandAt(1)->setRegister(*Normalized); - if (!SetSource) - return std::unexpected(SetSource.error()); - auto Rounding = appendImmediate(Instruction, 0); - if (!Rounding) - return std::unexpected(Rounding.error()); - const bool IsWord = Source->scalarBitWidth() <= 32; - if (Type->scalarBitWidth() == 32) { - return Instruction.setOpcode(targetOpcode( - IsWord ? (Unsigned ? InstructionOpcode::FCVT_S_WU : InstructionOpcode::FCVT_S_W) - : (Unsigned ? InstructionOpcode::FCVT_S_LU : InstructionOpcode::FCVT_S_L))); - } - return Instruction.setOpcode(targetOpcode( - IsWord ? (Unsigned ? InstructionOpcode::FCVT_D_WU : InstructionOpcode::FCVT_D_W) - : (Unsigned ? InstructionOpcode::FCVT_D_LU : InstructionOpcode::FCVT_D_L))); - } - - [[nodiscard]] static Expected<void> selectFloatingMinMax(MachineInstr &Instruction, - std::optional<LLT> Type, - InstructionOpcode Single, - InstructionOpcode Double) { - if (Type && Type->isFloat() && Type->scalarBitWidth() == 32) - return Instruction.setOpcode(targetOpcode(Single)); - if (Type && Type->isFloat() && Type->scalarBitWidth() == 64) - return Instruction.setOpcode(targetOpcode(Double)); - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V cannot select this floating min/max type"); - } - - [[nodiscard]] static Expected<void> selectFence(MachineInstr &Instruction) { - const auto Ordering = signedImmediate(*Instruction.operandAt(0)); - if (!Ordering) - return std::unexpected(Ordering.error()); - std::int64_t Pred = 0; - std::int64_t Succ = 0; - switch (*Ordering) { - case 4: - Pred = 1; - Succ = 3; - break; - case 5: - Pred = 3; - Succ = 2; - break; - case 6: - case 7: - Pred = 3; - Succ = 3; - break; - default: - return makeUnexpected(ErrorCode::Unsupported, "RISC-V fence ordering is unsupported"); - } - auto SetPred = replaceImmediate(Instruction, 0, Pred); - if (!SetPred) - return std::unexpected(SetPred.error()); - auto SetSucc = replaceImmediate(Instruction, 1, Succ); - if (!SetSucc) - return std::unexpected(SetSucc.error()); - return Instruction.setOpcode(targetOpcode(InstructionOpcode::FENCE)); - } - - [[nodiscard]] static Expected<void> selectIndirectBranch(MachineInstr &Instruction) { - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto Inserted = insertPhysicalRegister(Instruction, 0, *Zero, RegisterOperandFlag::Define); - if (!Inserted) - return std::unexpected(Inserted.error()); - auto Immediate = appendImmediate(Instruction, 0); - if (!Immediate) - return std::unexpected(Immediate.error()); - return Instruction.setOpcode(targetOpcode(InstructionOpcode::JALR)); - } - - [[nodiscard]] static Expected<void> selectTrap(MachineInstr &Instruction) { - while (Instruction.operandCount() != 0) { - auto Erased = Instruction.eraseOperand(Instruction.operandCount() - 1U); - if (!Erased) - return std::unexpected(Erased.error()); - } - return Instruction.setOpcode(targetOpcode(InstructionOpcode::EBREAK)); - } - - [[nodiscard]] static Expected<void> selectBitcast(MachineInstr &Instruction, - MachineFunction &Function) { - const std::optional<LLT> DestinationType = resultType(Function, Instruction); - const std::optional<LLT> SourceType = operandType(Function, Instruction, 1); - if (!DestinationType || !SourceType) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V bitcast has invalid register types"); - if (DestinationType->isInteger() && SourceType->isFloat()) { - return Instruction.setOpcode(targetOpcode(SourceType->scalarBitWidth() == 32 - ? InstructionOpcode::FMV_X_W - : InstructionOpcode::FMV_X_D)); - } - if (DestinationType->isFloat() && SourceType->isInteger()) { - return Instruction.setOpcode(targetOpcode(DestinationType->scalarBitWidth() == 32 - ? InstructionOpcode::FMV_W_X - : InstructionOpcode::FMV_D_X)); - } - return makeUnexpected(ErrorCode::Unsupported, "RISC-V bitcast pair is unsupported"); - } - - [[nodiscard]] static Expected<void> selectIntegerBinary(MachineInstr &Instruction, - std::optional<LLT> Type, - InstructionOpcode DoubleWord, - InstructionOpcode Word) { - if (!Type || !isSupportedIntegerType(*Type)) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V integer instruction requires a supported scalar integer"); - const bool IsWord = Type->isInteger() && Type->scalarBitWidth() <= 32; - return Instruction.setOpcode(targetOpcode(IsWord ? Word : DoubleWord)); - } - - [[nodiscard]] static Expected<void> - selectNormalizedIntegerBinary(MachineInstr &Instruction, MachineFunction &Function, - const TargetDescription &Target, std::optional<LLT> Type, - InstructionOpcode DoubleWord, InstructionOpcode Word, bool Signed, - bool NormalizeRight) { - const MachineOperand *LeftOperand = Instruction.operandAt(1); - const MachineOperand *RightOperand = Instruction.operandAt(2); - const Register *Left = LeftOperand == nullptr ? nullptr : LeftOperand->getIf<Register>(); - const Register *Right = RightOperand == nullptr ? nullptr : RightOperand->getIf<Register>(); - if (!Type || !isSupportedIntegerScalar(*Type) || Left == nullptr || Right == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V normalized integer operation has invalid operands"); - } - auto NormalizedLeft = - normalizeIntegerBefore(Instruction, Function, Target, *Left, *Type, Signed); - if (!NormalizedLeft) - return std::unexpected(NormalizedLeft.error()); - Register SelectedRight = *Right; - if (NormalizeRight) { - auto NormalizedRight = - normalizeIntegerBefore(Instruction, Function, Target, *Right, *Type, Signed); - if (!NormalizedRight) - return std::unexpected(NormalizedRight.error()); - SelectedRight = *NormalizedRight; - } - auto SetLeft = Instruction.operandAt(1)->setRegister(*NormalizedLeft); - if (!SetLeft) - return std::unexpected(SetLeft.error()); - auto SetRight = Instruction.operandAt(2)->setRegister(SelectedRight); - if (!SetRight) - return std::unexpected(SetRight.error()); - const bool IsWord = Type->scalarBitWidth() <= 32; - return Instruction.setOpcode(targetOpcode(IsWord ? Word : DoubleWord)); - } - - [[nodiscard]] static Expected<void> selectLoad(MachineInstr &Instruction, - std::optional<LLT> Type) { - if (!Type) - return makeUnexpected(ErrorCode::VerificationError, - "load result has no virtual-register type"); - const auto Bytes = memoryAccessBytes(Instruction); - if (!Bytes) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V load size does not match its result type"); - if (Instruction.memOperands().front()->isAtomic()) { - if (!isSupportedIntegerType(*Type) || (*Bytes != 4 && *Bytes != 8)) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V atomic load requires a 32-bit or 64-bit GPR value"); - return Instruction.setOpcode( - targetOpcode(*Bytes == 4 ? InstructionOpcode::LR_W : InstructionOpcode::LR_D)); - } - auto AppendOffset = appendImmediate(Instruction, 0); - if (!AppendOffset) - return std::unexpected(AppendOffset.error()); - if (isSupportedIntegerType(*Type)) { - const InstructionOpcode Selected = *Bytes == 1 ? InstructionOpcode::LB - : *Bytes == 2 ? InstructionOpcode::LH - : *Bytes == 4 ? InstructionOpcode::LW - : *Bytes == 8 ? InstructionOpcode::LD - : InstructionOpcode::Invalid; - if (Selected == InstructionOpcode::Invalid) - return makeUnexpected(ErrorCode::Unsupported, "RISC-V integer load width is unsupported"); - return Instruction.setOpcode(targetOpcode(Selected)); - } - if (Type->isFloat() && Type->scalarBitWidth() == 32) - return Instruction.setOpcode(targetOpcode(InstructionOpcode::FLW)); - if (Type->isFloat() && Type->scalarBitWidth() == 64) - return Instruction.setOpcode(targetOpcode(InstructionOpcode::FLD)); - return makeUnexpected(ErrorCode::Unsupported, "RISC-V cannot select this generic load type"); - } - - [[nodiscard]] static Expected<void> selectExtLoad(MachineInstr &Instruction, - std::optional<LLT> Type, bool Signed) { - if (!Type || !isSupportedIntegerType(*Type)) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V extension load requires an integer result"); - const auto Bytes = memoryAccessBytes(Instruction); - if (!Bytes) - return makeUnexpected(ErrorCode::Unsupported, "RISC-V extension load has an invalid width"); - const InstructionOpcode Selected = - *Bytes == 1 ? (Signed ? InstructionOpcode::LB : InstructionOpcode::LBU) - : *Bytes == 2 ? (Signed ? InstructionOpcode::LH : InstructionOpcode::LHU) - : *Bytes == 4 ? (Signed ? InstructionOpcode::LW : InstructionOpcode::LWU) - : InstructionOpcode::Invalid; - if (Selected == InstructionOpcode::Invalid) - return makeUnexpected(ErrorCode::Unsupported, "RISC-V extension load width is unsupported"); - auto Immediate = appendImmediate(Instruction, 0); - if (!Immediate) - return std::unexpected(Immediate.error()); - return Instruction.setOpcode(targetOpcode(Selected)); - } - - [[nodiscard]] static Expected<void> selectFloatExtLoad(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *Destination = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - if (Destination == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V floating extension load has no destination register"); - const Register DestinationRegister = *Destination; - auto F32 = LLT::makeFloat(FloatSemantics::IEEESingle); - if (!F32) - return std::unexpected(F32.error()); - auto Temporary = createTemporary(Function, *F32, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto SetDestination = Instruction.operandAt(0)->setRegister(*Temporary); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto Immediate = appendImmediate(Instruction, 0); - if (!Immediate) - return std::unexpected(Immediate.error()); - auto SetOpcode = Instruction.setOpcode(targetOpcode(InstructionOpcode::FLW)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - MachineIRBuilder Builder(Function, Target); - auto Conversion = buildImmediateAfter(Builder, Instruction, InstructionOpcode::FCVT_D_S, - DestinationRegister, *Temporary, 0); - if (!Conversion) - return std::unexpected(Conversion.error()); - return {}; - } - - [[nodiscard]] static Expected<void> selectFloatTruncStore(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target) { - const MachineOperand *ValueOperand = Instruction.operandAt(0); - const Register *Value = ValueOperand == nullptr ? nullptr : ValueOperand->getIf<Register>(); - if (Value == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V floating truncation store has no value register"); - const Register ValueRegister = *Value; - auto F32 = LLT::makeFloat(FloatSemantics::IEEESingle); - if (!F32) - return std::unexpected(F32.error()); - auto Temporary = createTemporary(Function, *F32, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - MachineIRBuilder Builder(Function, Target); - auto Point = Builder.setInsertPointBefore(Instruction); - if (!Point) - return std::unexpected(Point.error()); - auto Conversion = Builder.buildInstr(targetOpcode(InstructionOpcode::FCVT_S_D)); - if (!Conversion) - return std::unexpected(Conversion.error()); - auto Def = Conversion->addDef(*Temporary); - if (!Def) - return std::unexpected(Def.error()); - auto Use = Conversion->addUse(ValueRegister); - if (!Use) - return std::unexpected(Use.error()); - auto Rounding = Conversion->addImmediate(0); - if (!Rounding) - return std::unexpected(Rounding.error()); - auto Built = Conversion->build(); - if (!Built) - return std::unexpected(Built.error()); - auto SetValue = Instruction.operandAt(0)->setRegister(*Temporary); - if (!SetValue) - return std::unexpected(SetValue.error()); - auto Immediate = appendImmediate(Instruction, 0); - if (!Immediate) - return std::unexpected(Immediate.error()); - return Instruction.setOpcode(targetOpcode(InstructionOpcode::FSW)); - } - - [[nodiscard]] static Expected<void> selectStore(MachineInstr &Instruction, - MachineFunction &Function) { - const MachineOperand *Value = Instruction.operandAt(0); - const std::optional<LLT> Type = - Value == nullptr ? std::nullopt : registerType(Function, *Value); - if (!Type) - return makeUnexpected(ErrorCode::VerificationError, - "store value has no virtual-register type"); - const auto Bytes = memoryAccessBytes(Instruction); - if (!Bytes) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V store size does not match its value type"); - if (Instruction.memOperands().front()->isAtomic()) { - if (!isSupportedIntegerType(*Type) || (*Bytes != 4 && *Bytes != 8)) - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V atomic store requires a 32-bit or 64-bit GPR value"); - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto Destination = insertPhysicalRegister(Instruction, 0, *Zero, RegisterOperandFlag::Define); - if (!Destination) - return std::unexpected(Destination.error()); - return Instruction.setOpcode( - targetOpcode(*Bytes == 4 ? InstructionOpcode::AMOSWAP_W : InstructionOpcode::AMOSWAP_D)); - } - auto AppendOffset = appendImmediate(Instruction, 0); - if (!AppendOffset) - return std::unexpected(AppendOffset.error()); - if (isSupportedIntegerType(*Type)) { - const InstructionOpcode Selected = *Bytes == 1 ? InstructionOpcode::SB - : *Bytes == 2 ? InstructionOpcode::SH - : *Bytes == 4 ? InstructionOpcode::SW - : *Bytes == 8 ? InstructionOpcode::SD - : InstructionOpcode::Invalid; - if (Selected == InstructionOpcode::Invalid) - return makeUnexpected(ErrorCode::Unsupported, "RISC-V integer store width is unsupported"); - return Instruction.setOpcode(targetOpcode(Selected)); - } - if (Type->isFloat() && Type->scalarBitWidth() == 32) - return Instruction.setOpcode(targetOpcode(InstructionOpcode::FSW)); - if (Type->isFloat() && Type->scalarBitWidth() == 64) - return Instruction.setOpcode(targetOpcode(InstructionOpcode::FSD)); - return makeUnexpected(ErrorCode::Unsupported, "RISC-V cannot select this generic store type"); - } - - [[nodiscard]] static Expected<void> selectBranch(MachineInstr &Instruction) { - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto Inserted = insertPhysicalRegister(Instruction, 0, *Zero, RegisterOperandFlag::Define); - if (!Inserted) - return std::unexpected(Inserted.error()); - return Instruction.setOpcode(targetOpcode(InstructionOpcode::JAL)); - } - - [[nodiscard]] static Expected<void> selectConditionalBranch(MachineInstr &Instruction) { - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto Inserted = insertPhysicalRegister(Instruction, 1, *Zero, RegisterOperandFlag::Use); - if (!Inserted) - return std::unexpected(Inserted.error()); - return Instruction.setOpcode(targetOpcode(InstructionOpcode::BNE)); - } - - [[nodiscard]] static Expected<void> selectIntegerConstant(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - std::optional<LLT> Type) { - if (!Type || !isSupportedIntegerType(*Type)) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V constant selection requires an i1-i64 or pointer result"); - } - const MachineOperand *ImmediateOperand = Instruction.operandAt(1); - if (ImmediateOperand == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V generic constant has no immediate operand"); - auto ImmediateValue = signedImmediate(*ImmediateOperand); - if (!ImmediateValue) - return std::unexpected(ImmediateValue.error()); - const MachineOperand *DestinationOperand = Instruction.operandAt(0); - const Register *DestinationValue = - DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>(); - if (DestinationValue == nullptr || !DestinationValue->isVirtual()) { - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V generic constant must define a virtual register"); - } - const Register Destination = *DestinationValue; - const std::int64_t Value = *ImmediateValue; - const std::uint32_t ImmediateTargetFlags = ImmediateOperand->targetFlags(); - const bool IsWord = Type->isInteger() && Type->scalarBitWidth() <= 32; - const InstructionOpcode AddImmediate = - IsWord ? InstructionOpcode::ADDIW : InstructionOpcode::ADDI; - const InstructionOpcode ShiftLeftImmediate = - IsWord ? InstructionOpcode::SLLIW : InstructionOpcode::SLLI; - if (fitsSigned(Value, 12)) { - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto Source = insertPhysicalRegister(Instruction, 1, *Zero, RegisterOperandFlag::Use); - if (!Source) - return std::unexpected(Source.error()); - auto EraseImmediate = Instruction.eraseOperand(2); - if (!EraseImmediate) - return std::unexpected(EraseImmediate.error()); - auto NormalizeImmediate = Instruction.insertOperand( - 2, MachineOperand::immediate(Value, ImmediateTargetFlags)); - if (!NormalizeImmediate) - return std::unexpected(NormalizeImmediate.error()); - return Instruction.setOpcode(targetOpcode(AddImmediate)); - } - - // Recursively decompose the value into signed 12-bit chunks. The - // quotient is computed from an unsigned bit pattern with explicit sign - // extension so both INT64_MIN and INT64_MAX remain well-defined. - std::uint64_t Remaining = static_cast<std::uint64_t>(Value); - std::vector<std::int64_t> Digits; - while (!fitsSigned(std::bit_cast<std::int64_t>(Remaining), 12)) { - const std::uint64_t LowBits = Remaining & 0xFFFU; - std::int64_t Digit = static_cast<std::int64_t>(LowBits); - if (Digit >= 0x800) - Digit -= 0x1000; - Digits.push_back(Digit); - - const std::uint64_t Adjusted = Remaining - static_cast<std::uint64_t>(Digit); - std::uint64_t Quotient = Adjusted >> 12U; - if ((Remaining & (std::uint64_t{1} << 63U)) != 0) - Quotient |= ~std::uint64_t{0} << 52U; - Remaining = Quotient; - } - const std::int64_t Base = std::bit_cast<std::int64_t>(Remaining); - - auto Temporary = createTemporary(Function, *Type, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - auto *Parent = Instruction.parent(); - if (Parent == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V constant selection received a detached instruction"); - auto SetOpcode = Instruction.setOpcode(targetOpcode(AddImmediate)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - auto SetDestination = Instruction.operandAt(0)->setRegister(*Temporary); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto EraseImmediate = Instruction.eraseOperand(1); - if (!EraseImmediate) - return std::unexpected(EraseImmediate.error()); - auto Zero = physicalRegister(PhysicalRegisterID::X0); - if (!Zero) - return std::unexpected(Zero.error()); - auto AddSource = insertPhysicalRegister(Instruction, 1, *Zero, RegisterOperandFlag::Use); - if (!AddSource) - return std::unexpected(AddSource.error()); - auto AddBase = appendImmediate(Instruction, Base); - if (!AddBase) - return std::unexpected(AddBase.error()); - - MachineIRBuilder Builder(Function, Target); - auto InsertPoint = Builder.setInsertPointAfter(Instruction); - if (!InsertPoint) - return std::unexpected(InsertPoint.error()); - - Register Current = *Temporary; - for (std::size_t Reverse = Digits.size(); Reverse != 0; --Reverse) { - const std::size_t Index = Reverse - 1U; - auto ShiftResult = createTemporary(Function, *Type, Target); - if (!ShiftResult) - return std::unexpected(ShiftResult.error()); - auto Shift = Builder.buildInstr(targetOpcode(ShiftLeftImmediate)); - if (!Shift) - return std::unexpected(Shift.error()); - auto ShiftDef = Shift->addDef(*ShiftResult); - if (!ShiftDef) - return std::unexpected(ShiftDef.error()); - auto ShiftUse = Shift->addUse(Current); - if (!ShiftUse) - return std::unexpected(ShiftUse.error()); - auto ShiftAmount = Shift->addImmediate(12); - if (!ShiftAmount) - return std::unexpected(ShiftAmount.error()); - auto BuiltShift = Shift->build(); - if (!BuiltShift) - return std::unexpected(BuiltShift.error()); - - const bool IsFinal = Index == 0; - Register AddDestination; - if (IsFinal) { - AddDestination = Destination; - } else { - auto AddTemporary = createTemporary(Function, *Type, Target); - if (!AddTemporary) - return std::unexpected(AddTemporary.error()); - AddDestination = *AddTemporary; - } - auto Add = Builder.setInsertPointAfter(**BuiltShift); - if (!Add) - return std::unexpected(Add.error()); - auto AddInstruction = Builder.buildInstr(targetOpcode(AddImmediate)); - if (!AddInstruction) - return std::unexpected(AddInstruction.error()); - auto AddDef = AddInstruction->addDef(AddDestination); - if (!AddDef) - return std::unexpected(AddDef.error()); - auto AddUse = AddInstruction->addUse(*ShiftResult); - if (!AddUse) - return std::unexpected(AddUse.error()); - auto AddImmediateResult = AddInstruction->addImmediate(Digits[Index]); - if (!AddImmediateResult) - return std::unexpected(AddImmediateResult.error()); - auto BuiltAdd = AddInstruction->build(); - if (!BuiltAdd) - return std::unexpected(BuiltAdd.error()); - Current = AddDestination; - } - return {}; - } - - [[nodiscard]] static Expected<void> selectFloatingConstant(MachineInstr &Instruction, - MachineFunction &Function, - const TargetDescription &Target, - std::optional<LLT> Type) { - if (!Type || !isRVFloatType(*Type)) - return makeUnexpected(ErrorCode::Unsupported, "RISC-V floating constant requires f32 or f64"); - const MachineOperand *Immediate = Instruction.operandAt(1); - const auto *Floating = - Immediate == nullptr ? nullptr : Immediate->getIf<FloatingImmediateOperand>(); - const std::optional<FloatSemantics> Semantics = Type->floatSemantics(); - if (Floating == nullptr || !Semantics || Floating->Bits.semantics() != *Semantics) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V floating constant has mismatched bit semantics"); - 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 floating constant must define a virtual register"); - const Register DestinationRegister = *Destination; - - const std::uint32_t BitWidth = Type->scalarBitWidth(); - const std::uint64_t Bits = Floating->Bits.words().empty() ? 0 : Floating->Bits.words().front(); - auto Integer = IntegerImmediate::fromUnsigned(64, Bits); - if (!Integer) - return std::unexpected(Integer.error()); - auto IntegerType = LLT::makeInteger(64); - if (!IntegerType) - return std::unexpected(IntegerType.error()); - auto Temporary = createTemporary(Function, *IntegerType, Target); - if (!Temporary) - return std::unexpected(Temporary.error()); - - auto SetOpcode = Instruction.setOpcode(opcode(GenericOpcode::G_CONSTANT)); - if (!SetOpcode) - return std::unexpected(SetOpcode.error()); - auto SetDestination = Instruction.operandAt(0)->setRegister(*Temporary); - if (!SetDestination) - return std::unexpected(SetDestination.error()); - auto EraseImmediate = Instruction.eraseOperand(1); - if (!EraseImmediate) - return std::unexpected(EraseImmediate.error()); - auto AddInteger = Instruction.addOperand(MachineOperand::integerImmediate(*Integer)); - if (!AddInteger) - return std::unexpected(AddInteger.error()); - auto IntegerSelection = selectIntegerConstant(Instruction, Function, Target, *IntegerType); - if (!IntegerSelection) - return std::unexpected(IntegerSelection.error()); - - MachineInstr *IntegerDefinition = nullptr; - for (MachineOperand &DefOperand : Function.registerInfo().defOperands(*Temporary)) { - IntegerDefinition = DefOperand.parent(); - break; - } - if (IntegerDefinition == nullptr) - return makeUnexpected(ErrorCode::InternalError, - "RISC-V floating constant lost its integer materialization"); - MachineIRBuilder Builder(Function, Target); - auto InsertPoint = Builder.setInsertPointAfter(*IntegerDefinition); - if (!InsertPoint) - return std::unexpected(InsertPoint.error()); - const InstructionOpcode Move = - BitWidth == 32 ? InstructionOpcode::FMV_W_X : InstructionOpcode::FMV_D_X; - auto MoveInstruction = Builder.buildInstr(targetOpcode(Move)); - if (!MoveInstruction) - return std::unexpected(MoveInstruction.error()); - auto MoveDef = MoveInstruction->addDef(DestinationRegister); - if (!MoveDef) - return std::unexpected(MoveDef.error()); - auto MoveUse = MoveInstruction->addUse(*Temporary); - if (!MoveUse) - return std::unexpected(MoveUse.error()); - auto BuiltMove = MoveInstruction->build(); - if (!BuiltMove) - return std::unexpected(BuiltMove.error()); - return {}; - } - - [[nodiscard]] static Expected<void> selectRoundingBinary(MachineInstr &Instruction, - std::optional<LLT> Type, - InstructionOpcode Single, - InstructionOpcode Double) { - auto RoundingMode = appendImmediate(Instruction, 0); - if (!RoundingMode) - return std::unexpected(RoundingMode.error()); - if (Type && Type->isFloat() && Type->scalarBitWidth() == 32) - return Instruction.setOpcode(targetOpcode(Single)); - if (Type && Type->isFloat() && Type->scalarBitWidth() == 64) - return Instruction.setOpcode(targetOpcode(Double)); - return makeUnexpected(ErrorCode::Unsupported, "RISC-V cannot select this floating binary type"); - } - - [[nodiscard]] static Expected<void> selectRoundingTernary(MachineInstr &Instruction, - std::optional<LLT> Type, - InstructionOpcode Single, - InstructionOpcode Double) { - return selectRoundingBinary(Instruction, Type, Single, Double); - } - - [[nodiscard]] static Expected<void> selectRoundingUnary(MachineInstr &Instruction, - std::optional<LLT> Type, - InstructionOpcode Single, - InstructionOpcode Double) { - return selectRoundingBinary(Instruction, Type, Single, Double); - } - - [[nodiscard]] static Expected<void> selectSignInjection(MachineInstr &Instruction, - std::optional<LLT> Type, - InstructionOpcode Single, - InstructionOpcode Double) { - const MachineOperand *Source = Instruction.operandAt(1); - const Register *SourceReg = Source == nullptr ? nullptr : Source->getIf<Register>(); - if (SourceReg == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "RISC-V floating sign operation requires a register source"); - auto Duplicate = insertVirtualUse(Instruction, 2, *SourceReg); - if (!Duplicate) - return std::unexpected(Duplicate.error()); - if (Type && Type->isFloat() && Type->scalarBitWidth() == 32) - return Instruction.setOpcode(targetOpcode(Single)); - if (Type && Type->isFloat() && Type->scalarBitWidth() == 64) - return Instruction.setOpcode(targetOpcode(Double)); - return makeUnexpected(ErrorCode::Unsupported, "RISC-V cannot select this floating unary type"); - } -}; - -} // namespace - -const LegalizerInfo &legalizerInfo() noexcept { - static const RISCVLegalizerInfo Value(RISCVSubtargetFeatures::rv64gc()); - return Value; -} - -std::unique_ptr<LegalizerInfo> createLegalizerInfo(RISCVSubtargetFeatures Features) { - return std::make_unique<RISCVLegalizerInfo>(Features); -} - -const RegisterBankInfo ®isterBankInfo() noexcept { - static const RISCVRegisterBankInfo Value; - return Value; -} - -const InstructionSelector &instructionSelector() noexcept { - static const RISCVInstructionSelector Value; - return Value; -} - -} // namespace rcc::riscv |
