diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-14 04:33:41 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-14 04:33:41 +0800 |
| commit | 8be4d04455f70892a94d1e0798e266c948b4750a (patch) | |
| tree | 6ef304317a6980ecfbe32868d64cd05f5e8af8bd /backend/include/rcc/CodeGen/GlobalISel/LegalizationArtifactCombiner.h | |
| parent | aa69e15c6909c3b162b15f5326f0a72ba9bb7163 (diff) | |
| download | rcc-project-8be4d04455f70892a94d1e0798e266c948b4750a.tar.gz rcc-project-8be4d04455f70892a94d1e0798e266c948b4750a.tar.xz | |
[CodeGen] Match LLVM Legalizer ownership
Diffstat (limited to 'backend/include/rcc/CodeGen/GlobalISel/LegalizationArtifactCombiner.h')
| -rw-r--r-- | backend/include/rcc/CodeGen/GlobalISel/LegalizationArtifactCombiner.h | 1802 |
1 files changed, 1802 insertions, 0 deletions
diff --git a/backend/include/rcc/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/backend/include/rcc/CodeGen/GlobalISel/LegalizationArtifactCombiner.h new file mode 100644 index 0000000..c3f8d0b --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/LegalizationArtifactCombiner.h @@ -0,0 +1,1802 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_LEGALIZATIONARTIFACTCOMBINER_H +#define RCC_CODEGEN_GLOBALISEL_LEGALIZATIONARTIFACTCOMBINER_H + +#include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/GlobalISel/GenericMachineInstrs.h" +#include "rcc/CodeGen/GlobalISel/GISelChangeObserver.h" +#include "rcc/CodeGen/GlobalISel/LegalizerInfo.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineIRBuilder.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/MachineRegisterInfo.h" +#include "rcc/Support/Error.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <optional> +#include <ranges> +#include <span> +#include <unordered_set> +#include <utility> +#include <vector> + +namespace rcc { + +class LegalizationArtifactCombiner final { + MachineIRBuilder &Builder; + MachineRegisterInfo &MRI; + const LegalizerInfo &LI; + + [[nodiscard]] static bool isArtifactCast(Opcode InstructionOpcode) noexcept { + switch (static_cast<GenericOpcode>(InstructionOpcode.value())) { + case GenericOpcode::G_TRUNC: + case GenericOpcode::G_SEXT: + case GenericOpcode::G_ZEXT: + case GenericOpcode::G_ANYEXT: + return true; + default: + return false; + } + } + +public: + LegalizationArtifactCombiner(MachineIRBuilder &Builder, MachineRegisterInfo &MRI, + const LegalizerInfo &Info) + : Builder(Builder), MRI(MRI), LI(Info) {} + + [[nodiscard]] Expected<bool> + tryCombineInstruction(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + GISelObserverWrapper &Observer) { + if (!DeadInstructions.empty()) { + auto Deleted = deleteMarkedDeadInsts(DeadInstructions, Observer); + if (!Deleted) + return std::unexpected(Deleted.error()); + } + + std::vector<Register> UpdatedDefs; + Expected<bool> Changed = false; + switch (static_cast<GenericOpcode>(Instruction.opcode().value())) { + default: + return false; + case GenericOpcode::G_ANYEXT: + Changed = tryCombineAnyExt(Instruction, DeadInstructions, UpdatedDefs, Observer); + break; + case GenericOpcode::G_ZEXT: + Changed = tryCombineZExt(Instruction, DeadInstructions, UpdatedDefs, Observer); + break; + case GenericOpcode::G_SEXT: + Changed = tryCombineSExt(Instruction, DeadInstructions, UpdatedDefs, Observer); + break; + case GenericOpcode::G_UNMERGE_VALUES: + Changed = tryCombineUnmergeValues(Instruction, DeadInstructions, UpdatedDefs, Observer); + break; + case GenericOpcode::G_MERGE_VALUES: + case GenericOpcode::G_BUILD_VECTOR: + case GenericOpcode::G_CONCAT_VECTORS: + Changed = tryCombineMergeLike(Instruction, DeadInstructions, UpdatedDefs, Observer); + break; + case GenericOpcode::G_EXTRACT: + Changed = tryCombineExtract(Instruction, DeadInstructions, UpdatedDefs); + break; + case GenericOpcode::G_TRUNC: + Changed = tryCombineTrunc(Instruction, DeadInstructions, UpdatedDefs, Observer); + if (Changed && !*Changed) + UpdatedDefs.push_back(*Instruction.operandAt(0)->registerValue()); + break; + } + if (!Changed) + return std::unexpected(Changed.error()); + + while (!UpdatedDefs.empty()) { + const Register NewDef = UpdatedDefs.back(); + UpdatedDefs.pop_back(); + std::vector<MachineInstr *> Uses; + for (MachineOperand &Use : MRI.useOperands(NewDef)) { + if (Use.parent() != nullptr) + Uses.push_back(Use.parent()); + } + for (MachineInstr *Use : Uses) { + switch (static_cast<GenericOpcode>(Use->opcode().value())) { + case GenericOpcode::G_ANYEXT: + case GenericOpcode::G_ZEXT: + case GenericOpcode::G_SEXT: + case GenericOpcode::G_UNMERGE_VALUES: + case GenericOpcode::G_EXTRACT: + case GenericOpcode::G_TRUNC: + case GenericOpcode::G_BUILD_VECTOR: + Observer.changedInstr(*Use); + break; + case GenericOpcode::G_ASSERT_SEXT: + case GenericOpcode::G_ASSERT_ZEXT: + case GenericOpcode::G_ASSERT_ALIGN: + if (const auto Copy = Use->operandAt(0)->registerValue(); + Copy && Copy->isVirtual()) + UpdatedDefs.push_back(*Copy); + break; + default: + if (Use->opcode() == opcode(GenericOpcode::COPY)) { + if (const auto Copy = Use->operandAt(0)->registerValue(); + Copy && Copy->isVirtual()) + UpdatedDefs.push_back(*Copy); + } + break; + } + } + } + return *Changed; + } + +private: + class ArtifactValueFinder final { + public: + explicit ArtifactValueFinder(LegalizationArtifactCombiner &Combiner) + : Combiner(Combiner) {} + + [[nodiscard]] Expected<Register> findValueFromDef(Register DefReg, + std::uint64_t StartBit, + std::uint64_t Size, + LowLevelType DestinationType); + [[nodiscard]] Expected<bool> + tryCombineUnmergeDefs(GUnmerge &Instruction, + GISelChangeObserver &Observer, + std::vector<Register> &UpdatedDefs); + [[nodiscard]] Expected<GUnmerge *> + findUnmergeThatDefinesReg(Register Reg, std::uint64_t Size, + std::size_t &DefOperandIndex); + [[nodiscard]] Expected<bool> + isSequenceFromUnmerge(GMergeLikeInstr &Instruction, + std::size_t MergeStartIndex, GUnmerge *Unmerge, + std::size_t UnmergeStartIndex, + std::size_t ElementCount, std::uint64_t ElementSize, + bool AllowUndef); + [[nodiscard]] Expected<bool> + tryCombineMergeLike(GMergeLikeInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelChangeObserver &Observer); + + private: + [[nodiscard]] Expected<Register> + findValueFromDefImpl(Register DefReg, std::uint64_t StartBit, + std::uint64_t Size, LowLevelType DestinationType); + [[nodiscard]] Expected<Register> + findValueFromConcat(GConcatVectors &Instruction, + std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType); + [[nodiscard]] Expected<Register> + findValueFromBuildVector(GBuildVector &Instruction, + std::uint64_t StartBit, std::uint64_t Size); + [[nodiscard]] Expected<Register> + findValueFromInsert(MachineInstr &Instruction, std::uint64_t StartBit, + std::uint64_t Size, LowLevelType DestinationType); + [[nodiscard]] Expected<Register> + findValueFromExtension(MachineInstr &Instruction, + std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType); + [[nodiscard]] Expected<Register> + findValueFromTrunc(MachineInstr &Instruction, std::uint64_t StartBit, + std::uint64_t Size, LowLevelType DestinationType); + + LegalizationArtifactCombiner &Combiner; + Register CurrentBest; + std::unordered_set<Register> Visited; + }; + + [[nodiscard]] Expected<bool> + tryCombineAnyExt(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> + tryCombineZExt(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> + tryCombineSExt(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> + tryCombineTrunc(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> + tryCombineUnmergeValues(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> + tryCombineExtract(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs); + [[nodiscard]] Expected<bool> + tryCombineMergeLike(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> tryFoldImplicitDef( + MachineInstr &Instruction, std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer); + + [[nodiscard]] Expected<MachineInstr *> buildUnary(GenericOpcode InstructionOpcode, + Register Destination, + Register Source); + [[nodiscard]] Expected<MachineInstr *> + buildMergeLike(Register Destination, const std::vector<Register> &Sources); + [[nodiscard]] Expected<void> buildConstant(Register Destination, APInt Value); + [[nodiscard]] Expected<Register> buildConstant(LowLevelType Type, APInt Value); + [[nodiscard]] Expected<bool> isConstantUnsupported(LowLevelType Type) const; + [[nodiscard]] static std::optional<std::int64_t> + immediateOperand(const MachineInstr &Instruction, std::size_t Index); + [[nodiscard]] static std::size_t getDefIndex(const MachineInstr &Instruction, + Register SearchDef); + [[nodiscard]] Expected<std::pair<MachineInstr *, Register>> + definitionIgnoringCopies(Register Reg) const; + [[nodiscard]] Expected<std::vector<Register>> + buildOrFindUnmerge(LowLevelType DestinationType, Register Source); + [[nodiscard]] Expected<LowLevelType> divideType(LowLevelType Type, + std::size_t Parts) const; + [[nodiscard]] Expected<bool> + tryFoldUnmergeCast(MachineInstr &Instruction, MachineInstr &Cast, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs); + [[nodiscard]] static bool canFoldMergeOpcode(Opcode MergeOpcode, + std::optional<Opcode> ConvertOpcode, + LowLevelType OperandType, + LowLevelType DestinationType); + + [[nodiscard]] Expected<void> + replaceRegOrBuildCopy(Register Destination, Register Source, + std::vector<Register> &UpdatedDefs, + GISelChangeObserver &Observer) { + auto DestinationType = MRI.getType(Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = MRI.getType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + if (*DestinationType != *SourceType) { + auto Copy = Builder.buildCopy(Destination, Source); + if (!Copy) + return std::unexpected(Copy.error()); + UpdatedDefs.push_back(Destination); + return {}; + } + + std::vector<MachineInstr *> Uses; + for (MachineOperand &Use : MRI.useOperands(Destination)) { + if (Use.parent() == nullptr) + continue; + Uses.push_back(Use.parent()); + Observer.changingInstr(*Use.parent()); + } + auto Replaced = MRI.replaceRegWith(Destination, Source); + if (!Replaced) + return std::unexpected(Replaced.error()); + UpdatedDefs.push_back(Source); + for (MachineInstr *Use : Uses) + Observer.changedInstr(*Use); + return {}; + } + + [[nodiscard]] Register lookThroughCopyInstrs(Register Reg) const { + while (MachineInstr *Definition = MRI.getVRegDef(Reg)) { + if (Definition->opcode() != opcode(GenericOpcode::COPY) || + Definition->operandCount() < 2) + break; + const auto Source = Definition->operandAt(1)->registerValue(); + if (!Source || !Source->isVirtual()) + break; + Reg = *Source; + } + return Reg; + } + + [[nodiscard]] static std::optional<Register> + registerOperand(const MachineInstr &Instruction, std::size_t Index) { + const MachineOperand *Operand = Instruction.operandAt(Index); + return Operand == nullptr ? std::nullopt : Operand->registerValue(); + } + + [[nodiscard]] static bool hasOpcode(const MachineInstr *Instruction, + GenericOpcode ExpectedOpcode) noexcept { + return Instruction != nullptr && Instruction->opcode() == opcode(ExpectedOpcode); + } + + void markDefDead(MachineInstr &Instruction, MachineInstr &Definition, + std::vector<MachineInstr *> &DeadInstructions, + std::size_t DefIndex = 0) { + MachineInstr *Previous = &Instruction; + while (Previous != &Definition) { + const auto Source = artifactSourceRegister(*Previous); + if (!Source) + break; + MachineInstr *SourceDefinition = MRI.getVRegDef(*Source); + if (SourceDefinition == nullptr || !MRI.hasOneUse(*Source)) + break; + if (SourceDefinition != &Definition) + DeadInstructions.push_back(SourceDefinition); + Previous = SourceDefinition; + } + if (Previous != &Definition) + return; + + std::size_t CurrentDef = 0; + for (const MachineOperand &Operand : Definition.operands()) { + if (!Operand.isDef()) + continue; + const auto Reg = Operand.registerValue(); + if (!Reg) + return; + if (CurrentDef == DefIndex) { + if (!MRI.hasOneUse(*Reg)) + return; + } else if (!MRI.useEmpty(*Reg)) { + return; + } + ++CurrentDef; + } + DeadInstructions.push_back(&Definition); + } + + void markInstAndDefDead(MachineInstr &Instruction, MachineInstr &Definition, + std::vector<MachineInstr *> &DeadInstructions, + std::size_t DefIndex = 0) { + DeadInstructions.push_back(&Instruction); + markDefDead(Instruction, Definition, DeadInstructions, DefIndex); + } + + [[nodiscard]] Expected<void> + deleteMarkedDeadInsts(std::vector<MachineInstr *> &DeadInstructions, + GISelObserverWrapper &) { + for (MachineInstr *Dead : DeadInstructions) { + if (Dead == nullptr || Dead->parent() == nullptr) + continue; + auto Removed = Dead->parent()->removeInstruction(*Dead); + if (!Removed) + return std::unexpected(Removed.error()); + } + DeadInstructions.clear(); + return {}; + } + + [[nodiscard]] static std::optional<Register> + artifactSourceRegister(const MachineInstr &Instruction) { + switch (static_cast<GenericOpcode>(Instruction.opcode().value())) { + case GenericOpcode::G_TRUNC: + case GenericOpcode::G_ZEXT: + case GenericOpcode::G_ANYEXT: + case GenericOpcode::G_SEXT: + case GenericOpcode::G_EXTRACT: + case GenericOpcode::G_ASSERT_SEXT: + case GenericOpcode::G_ASSERT_ZEXT: + case GenericOpcode::G_ASSERT_ALIGN: + return registerOperand(Instruction, 1); + case GenericOpcode::G_UNMERGE_VALUES: + return registerOperand(Instruction, Instruction.operandCount() - 1); + default: + if (Instruction.opcode() == opcode(GenericOpcode::COPY)) + return registerOperand(Instruction, 1); + return std::nullopt; + } + } + + [[nodiscard]] Expected<bool> isInstUnsupported(LegalizationQuery Query) const { + return LI.isUnsupported(Query); + } + + [[nodiscard]] Expected<bool> isInstLegal(LegalizationQuery Query) const { + return LI.isLegal(Query); + } + +}; + +inline Expected<MachineInstr *> +LegalizationArtifactCombiner::buildUnary(GenericOpcode InstructionOpcode, + Register Destination, + Register Source) { + auto Pending = Builder.buildInstr(opcode(InstructionOpcode)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(Destination); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Source); !Added) + return std::unexpected(Added.error()); + return Pending->build(); +} + +inline Expected<MachineInstr *> LegalizationArtifactCombiner::buildMergeLike( + Register Destination, const std::vector<Register> &Sources) { + if (Sources.empty()) + return makeUnexpected(ErrorCode::InvalidArgument, + "merge-like instruction requires source registers"); + auto DestinationType = MRI.getType(Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = MRI.getType(Sources.front()); + if (!SourceType) + return std::unexpected(SourceType.error()); + + GenericOpcode InstructionOpcode = GenericOpcode::G_MERGE_VALUES; + if (DestinationType->isVector()) + InstructionOpcode = SourceType->isVector() ? GenericOpcode::G_CONCAT_VECTORS + : GenericOpcode::G_BUILD_VECTOR; + auto Pending = Builder.buildInstr(opcode(InstructionOpcode)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(Destination); !Added) + return std::unexpected(Added.error()); + for (Register Source : Sources) { + if (auto Added = Pending->addUse(Source); !Added) + return std::unexpected(Added.error()); + } + return Pending->build(); +} + +inline Expected<void> +LegalizationArtifactCombiner::buildConstant(Register Destination, APInt Value) { + auto Built = Builder.buildConstant(Destination, std::move(Value)); + if (!Built) + return std::unexpected(Built.error()); + return {}; +} + +inline Expected<Register> +LegalizationArtifactCombiner::buildConstant(LowLevelType Type, APInt Value) { + auto Destination = MRI.createVirtualRegister(Type); + if (!Destination) + return std::unexpected(Destination.error()); + auto Built = buildConstant(*Destination, std::move(Value)); + if (!Built) + return std::unexpected(Built.error()); + return *Destination; +} + +inline Expected<bool> +LegalizationArtifactCombiner::isConstantUnsupported(LowLevelType Type) const { + if (!Type.isVector()) + return isInstUnsupported( + LegalizationQuery(opcode(GenericOpcode::G_CONSTANT), {Type})); + const auto ElementType = Type.scalarElementType(); + if (!ElementType) + return true; + auto ConstantUnsupported = isInstUnsupported( + LegalizationQuery(opcode(GenericOpcode::G_CONSTANT), {*ElementType})); + if (!ConstantUnsupported || *ConstantUnsupported) + return ConstantUnsupported; + return isInstUnsupported(LegalizationQuery( + opcode(GenericOpcode::G_BUILD_VECTOR), {Type, *ElementType})); +} + +inline std::optional<std::int64_t> +LegalizationArtifactCombiner::immediateOperand(const MachineInstr &Instruction, + std::size_t Index) { + const MachineOperand *Operand = Instruction.operandAt(Index); + if (Operand == nullptr) + return std::nullopt; + if (const auto *Value = Operand->getIf<std::int64_t>()) + return *Value; + return std::nullopt; +} + +inline std::size_t +LegalizationArtifactCombiner::getDefIndex(const MachineInstr &Instruction, + Register SearchDef) { + std::size_t Index = 0; + for (const MachineOperand &Operand : Instruction.operands()) { + if (!Operand.isDef()) + continue; + if (Operand.registerValue() == SearchDef) + return Index; + ++Index; + } + return Index; +} + +inline Expected<std::pair<MachineInstr *, Register>> +LegalizationArtifactCombiner::definitionIgnoringCopies(Register Reg) const { + std::unordered_set<Register> Visited; + while (Reg.isVirtual() && Visited.insert(Reg).second) { + MachineInstr *Definition = MRI.getVRegDef(Reg); + if (Definition == nullptr) + return std::pair<MachineInstr *, Register>{nullptr, Reg}; + if (Definition->opcode() != opcode(GenericOpcode::COPY)) + return std::pair{Definition, Reg}; + const auto Source = registerOperand(*Definition, 1); + if (!Source) + return std::pair{Definition, Reg}; + Reg = *Source; + } + return std::pair<MachineInstr *, Register>{nullptr, Reg}; +} + +inline Expected<LowLevelType> +LegalizationArtifactCombiner::divideType(LowLevelType Type, + std::size_t Parts) const { + if (Parts == 0 || Type.minimumBitWidth() % Parts != 0) + return makeUnexpected(ErrorCode::InvalidArgument, + "LowLevelType cannot be divided into equal parts"); + if (!Type.isVector()) + return LowLevelType::makeCategoryNeutralScalar(Type.scalarBitWidth() / Parts); + if (Type.minimumElementCount() % Parts != 0) + return makeUnexpected(ErrorCode::InvalidArgument, + "vector element count cannot be divided into equal parts"); + const auto ElementType = Type.scalarElementType(); + if (!ElementType) + return makeUnexpected(ErrorCode::InvalidArgument, + "vector has no scalar element type"); + const std::uint32_t Elements = + Type.minimumElementCount() / static_cast<std::uint32_t>(Parts); + if (Elements == 1) + return *ElementType; + return Type.isScalableVector() + ? LowLevelType::makeScalableVector(Elements, *ElementType) + : LowLevelType::makeFixedVector(Elements, *ElementType); +} + +inline Expected<std::vector<Register>> +LegalizationArtifactCombiner::buildOrFindUnmerge(LowLevelType DestinationType, + Register Source) { + auto SourceType = MRI.getType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + if (DestinationType.minimumBitWidth() == 0 || + SourceType->minimumBitWidth() % DestinationType.minimumBitWidth() != 0) + return makeUnexpected(ErrorCode::InvalidArgument, + "unmerge destination does not tile the source type"); + const std::size_t Count = static_cast<std::size_t>( + SourceType->minimumBitWidth() / DestinationType.minimumBitWidth()); + + for (MachineOperand &Use : MRI.useOperands(Source)) { + MachineInstr *Candidate = Use.parent(); + if (!GUnmerge::classof(Candidate) || Candidate->operandCount() != Count + 1) + continue; + std::vector<Register> Existing; + Existing.reserve(Count); + bool Matches = true; + for (std::size_t Index = 0; Index < Count; ++Index) { + const auto Reg = registerOperand(*Candidate, Index); + if (!Reg) { + Matches = false; + break; + } + auto Type = MRI.getType(*Reg); + if (!Type || *Type != DestinationType) { + Matches = false; + break; + } + Existing.push_back(*Reg); + } + if (Matches) + return Existing; + } + + std::vector<Register> Destinations; + Destinations.reserve(Count); + for (std::size_t Index = 0; Index < Count; ++Index) { + auto Destination = MRI.createVirtualRegister(DestinationType); + if (!Destination) + return std::unexpected(Destination.error()); + Destinations.push_back(*Destination); + } + auto Built = Builder.buildUnmergeValues(Destinations, Source); + if (!Built) + return std::unexpected(Built.error()); + return Destinations; +} + +inline bool LegalizationArtifactCombiner::canFoldMergeOpcode( + Opcode MergeOpcode, std::optional<Opcode> ConvertOpcode, + LowLevelType OperandType, LowLevelType DestinationType) { + const auto Generic = static_cast<GenericOpcode>(MergeOpcode.value()); + if (Generic == GenericOpcode::G_BUILD_VECTOR || + Generic == GenericOpcode::G_MERGE_VALUES) { + if (!ConvertOpcode) + return true; + const auto ElementType = OperandType.scalarElementType(); + return !DestinationType.isVector() && OperandType.isVector() && + ElementType && DestinationType == *ElementType; + } + if (Generic != GenericOpcode::G_CONCAT_VECTORS) + return false; + if (!ConvertOpcode) + return true; + if (!DestinationType.isVector()) + return false; + const auto ElementType = OperandType.scalarElementType(); + if (!ElementType) + return false; + if (*ConvertOpcode == opcode(GenericOpcode::G_TRUNC)) + return DestinationType.minimumBitWidth() <= ElementType->minimumBitWidth(); + return DestinationType.minimumBitWidth() >= ElementType->minimumBitWidth(); +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineAnyExt( + MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + const auto Destination = registerOperand(Instruction, 0); + const auto OriginalSource = registerOperand(Instruction, 1); + if (!Destination || !OriginalSource) + return makeUnexpected(ErrorCode::VerificationError, + "G_ANYEXT has invalid register operands"); + const Register Source = lookThroughCopyInstrs(*OriginalSource); + MachineInstr *SourceDefinition = MRI.getVRegDef(Source); + if (SourceDefinition == nullptr) + return false; + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + + if (hasOpcode(SourceDefinition, GenericOpcode::G_TRUNC)) { + const auto TruncSource = registerOperand(*SourceDefinition, 1); + if (!TruncSource) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto TruncSourceType = MRI.getType(*TruncSource); + if (!TruncSourceType) + return std::unexpected(TruncSourceType.error()); + if (*DestinationType == *TruncSourceType) { + auto Replaced = replaceRegOrBuildCopy(*Destination, *TruncSource, + UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + } else { + auto Built = Builder.buildAnyExtOrTrunc(*Destination, *TruncSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + } + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_ANYEXT) || + hasOpcode(SourceDefinition, GenericOpcode::G_SEXT) || + hasOpcode(SourceDefinition, GenericOpcode::G_ZEXT)) { + const auto ExtensionSource = registerOperand(*SourceDefinition, 1); + if (!ExtensionSource) + return false; + auto Built = buildUnary( + static_cast<GenericOpcode>(SourceDefinition->opcode().value()), + *Destination, *ExtensionSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_CONSTANT)) { + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto Legal = isInstLegal(LegalizationQuery( + opcode(GenericOpcode::G_CONSTANT), {*DestinationType})); + if (!Legal) + return std::unexpected(Legal.error()); + const auto *Immediate = + SourceDefinition->operandAt(1)->getIf<IntegerImmediateOperand>(); + if (*Legal && Immediate != nullptr) { + auto Extended = Immediate->Value.sext(DestinationType->scalarBitWidth()); + if (!Extended) + return std::unexpected(Extended.error()); + auto Built = buildConstant(*Destination, std::move(*Extended)); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + } + return tryFoldImplicitDef(Instruction, DeadInstructions, UpdatedDefs, + Observer); +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineZExt( + MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + const auto Destination = registerOperand(Instruction, 0); + const auto OriginalSource = registerOperand(Instruction, 1); + if (!Destination || !OriginalSource) + return makeUnexpected(ErrorCode::VerificationError, + "G_ZEXT has invalid register operands"); + const Register Source = lookThroughCopyInstrs(*OriginalSource); + MachineInstr *SourceDefinition = MRI.getVRegDef(Source); + if (SourceDefinition == nullptr) + return false; + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + + if (hasOpcode(SourceDefinition, GenericOpcode::G_TRUNC) || + hasOpcode(SourceDefinition, GenericOpcode::G_SEXT)) { + const auto NarrowSource = registerOperand(*SourceDefinition, 1); + if (!NarrowSource) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = MRI.getType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + auto AndUnsupported = isInstUnsupported( + LegalizationQuery(opcode(GenericOpcode::G_AND), {*DestinationType})); + if (!AndUnsupported) + return std::unexpected(AndUnsupported.error()); + auto ConstantUnsupported = isConstantUnsupported(*DestinationType); + if (!ConstantUnsupported) + return std::unexpected(ConstantUnsupported.error()); + if (*AndUnsupported || *ConstantUnsupported) + return false; + + Register WideSource = *NarrowSource; + auto WideSourceType = MRI.getType(WideSource); + if (!WideSourceType) + return std::unexpected(WideSourceType.error()); + if (*WideSourceType != *DestinationType) { + auto Wide = MRI.createVirtualRegister(*DestinationType); + if (!Wide) + return std::unexpected(Wide.error()); + auto Built = hasOpcode(SourceDefinition, GenericOpcode::G_SEXT) + ? Builder.buildSExtOrTrunc(*Wide, WideSource) + : Builder.buildAnyExtOrTrunc(*Wide, WideSource); + if (!Built) + return std::unexpected(Built.error()); + WideSource = *Wide; + } + auto Mask = APInt::getLowBitsSet(DestinationType->scalarBitWidth(), + SourceType->scalarBitWidth()); + if (!Mask) + return std::unexpected(Mask.error()); + auto MaskRegister = buildConstant(*DestinationType, std::move(*Mask)); + if (!MaskRegister) + return std::unexpected(MaskRegister.error()); + auto Built = Builder.buildAnd(*Destination, WideSource, *MaskRegister); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_ZEXT)) { + const auto ExtensionSource = registerOperand(*SourceDefinition, 1); + if (!ExtensionSource) + return false; + Observer.changingInstr(Instruction); + auto Updated = Instruction.operandAt(1)->setRegister(*ExtensionSource); + if (!Updated) + return std::unexpected(Updated.error()); + Observer.changedInstr(Instruction); + UpdatedDefs.push_back(*Destination); + markDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_CONSTANT)) { + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto Legal = isInstLegal(LegalizationQuery( + opcode(GenericOpcode::G_CONSTANT), {*DestinationType})); + if (!Legal) + return std::unexpected(Legal.error()); + const auto *Immediate = + SourceDefinition->operandAt(1)->getIf<IntegerImmediateOperand>(); + if (*Legal && Immediate != nullptr) { + auto Extended = Immediate->Value.zext(DestinationType->scalarBitWidth()); + if (!Extended) + return std::unexpected(Extended.error()); + auto Built = buildConstant(*Destination, std::move(*Extended)); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + } + return tryFoldImplicitDef(Instruction, DeadInstructions, UpdatedDefs, + Observer); +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineSExt( + MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + const auto Destination = registerOperand(Instruction, 0); + const auto OriginalSource = registerOperand(Instruction, 1); + if (!Destination || !OriginalSource) + return makeUnexpected(ErrorCode::VerificationError, + "G_SEXT has invalid register operands"); + const Register Source = lookThroughCopyInstrs(*OriginalSource); + MachineInstr *SourceDefinition = MRI.getVRegDef(Source); + if (SourceDefinition == nullptr) + return false; + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + + if (hasOpcode(SourceDefinition, GenericOpcode::G_TRUNC)) { + const auto TruncSource = registerOperand(*SourceDefinition, 1); + if (!TruncSource) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = MRI.getType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + auto Unsupported = isInstUnsupported(LegalizationQuery( + opcode(GenericOpcode::G_SEXT_INREG), {*DestinationType})); + if (!Unsupported) + return std::unexpected(Unsupported.error()); + if (*Unsupported) + return false; + Register WideSource = *TruncSource; + auto WideSourceType = MRI.getType(WideSource); + if (!WideSourceType) + return std::unexpected(WideSourceType.error()); + if (*WideSourceType != *DestinationType) { + auto Wide = MRI.createVirtualRegister(*DestinationType); + if (!Wide) + return std::unexpected(Wide.error()); + auto Built = Builder.buildAnyExtOrTrunc(*Wide, WideSource); + if (!Built) + return std::unexpected(Built.error()); + WideSource = *Wide; + } + auto Built = Builder.buildSExtInReg(*Destination, WideSource, + SourceType->scalarBitWidth()); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_ZEXT) || + hasOpcode(SourceDefinition, GenericOpcode::G_SEXT)) { + const auto ExtensionSource = registerOperand(*SourceDefinition, 1); + if (!ExtensionSource) + return false; + auto Built = buildUnary( + static_cast<GenericOpcode>(SourceDefinition->opcode().value()), + *Destination, *ExtensionSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_CONSTANT)) { + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto Legal = isInstLegal(LegalizationQuery( + opcode(GenericOpcode::G_CONSTANT), {*DestinationType})); + if (!Legal) + return std::unexpected(Legal.error()); + const auto *Immediate = + SourceDefinition->operandAt(1)->getIf<IntegerImmediateOperand>(); + if (*Legal && Immediate != nullptr) { + auto Extended = Immediate->Value.sext(DestinationType->scalarBitWidth()); + if (!Extended) + return std::unexpected(Extended.error()); + auto Built = buildConstant(*Destination, std::move(*Extended)); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + } + return tryFoldImplicitDef(Instruction, DeadInstructions, UpdatedDefs, + Observer); +} + +inline Expected<bool> LegalizationArtifactCombiner::tryFoldImplicitDef( + MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &) { + const auto Destination = registerOperand(Instruction, 0); + const auto Source = registerOperand(Instruction, 1); + if (!Destination || !Source) + return false; + MachineInstr *Definition = MRI.getVRegDef(lookThroughCopyInstrs(*Source)); + if (!hasOpcode(Definition, GenericOpcode::G_IMPLICIT_DEF)) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + if (Instruction.opcode() == opcode(GenericOpcode::G_ANYEXT)) { + auto Legal = isInstLegal(LegalizationQuery( + opcode(GenericOpcode::G_IMPLICIT_DEF), {*DestinationType})); + if (!Legal) + return std::unexpected(Legal.error()); + if (!*Legal) + return false; + auto Built = Builder.buildImplicitDef(*Destination); + if (!Built) + return std::unexpected(Built.error()); + } else { + auto Unsupported = isConstantUnsupported(*DestinationType); + if (!Unsupported) + return std::unexpected(Unsupported.error()); + if (*Unsupported) + return false; + auto Built = Builder.buildConstant(*Destination, 0); + if (!Built) + return std::unexpected(Built.error()); + } + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *Definition, DeadInstructions); + return true; +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineTrunc( + MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + const auto Destination = registerOperand(Instruction, 0); + const auto OriginalSource = registerOperand(Instruction, 1); + if (!Destination || !OriginalSource) + return makeUnexpected(ErrorCode::VerificationError, + "G_TRUNC has invalid register operands"); + const Register Source = lookThroughCopyInstrs(*OriginalSource); + MachineInstr *SourceDefinition = MRI.getVRegDef(Source); + if (SourceDefinition == nullptr) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + + if (hasOpcode(SourceDefinition, GenericOpcode::G_CONSTANT)) { + auto Legal = isInstLegal(LegalizationQuery( + opcode(GenericOpcode::G_CONSTANT), {*DestinationType})); + if (!Legal) + return std::unexpected(Legal.error()); + const auto *Immediate = + SourceDefinition->operandAt(1)->getIf<IntegerImmediateOperand>(); + if (*Legal && Immediate != nullptr) { + auto Truncated = Immediate->Value.trunc(DestinationType->scalarBitWidth()); + if (!Truncated) + return std::unexpected(Truncated.error()); + auto Built = buildConstant(*Destination, std::move(*Truncated)); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + } + + if (GMerge::classof(SourceDefinition)) { + auto &Merge = castGenericMachineInstr<GMerge>(*SourceDefinition); + if (Merge.getNumSources() == 0) + return false; + const Register FirstSource = Merge.getSourceReg(0); + auto FirstSourceType = MRI.getType(FirstSource); + if (!FirstSourceType) + return std::unexpected(FirstSourceType.error()); + if (!DestinationType->isCategoryNeutralScalar() || + !FirstSourceType->isCategoryNeutralScalar()) + return false; + const std::uint64_t DestinationSize = DestinationType->minimumBitWidth(); + const std::uint64_t SourceSize = FirstSourceType->minimumBitWidth(); + if (DestinationSize < SourceSize) { + auto Unsupported = isInstUnsupported(LegalizationQuery( + opcode(GenericOpcode::G_TRUNC), + {*DestinationType, *FirstSourceType})); + if (!Unsupported) + return std::unexpected(Unsupported.error()); + if (*Unsupported) + return false; + auto Built = Builder.buildTrunc(*Destination, FirstSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + } else if (DestinationSize == SourceSize) { + auto Replaced = replaceRegOrBuildCopy(*Destination, FirstSource, + UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + } else if (DestinationSize % SourceSize == 0) { + const std::size_t SourceCount = + static_cast<std::size_t>(DestinationSize / SourceSize); + if (SourceCount >= Merge.getNumSources()) + return false; + auto Unsupported = isInstUnsupported(LegalizationQuery( + opcode(GenericOpcode::G_MERGE_VALUES), + {*DestinationType, *FirstSourceType})); + if (!Unsupported) + return std::unexpected(Unsupported.error()); + if (*Unsupported) + return false; + std::vector<Register> Sources; + Sources.reserve(SourceCount); + for (std::size_t Index = 0; Index < SourceCount; ++Index) + Sources.push_back(Merge.getSourceReg(Index)); + auto Built = Builder.buildMergeValues(*Destination, Sources); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + } else { + return false; + } + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_TRUNC)) { + const auto TruncSource = registerOperand(*SourceDefinition, 1); + if (!TruncSource) + return false; + auto Built = Builder.buildTrunc(*Destination, *TruncSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + ArtifactValueFinder Finder(*this); + auto Found = Finder.findValueFromDef(*Destination, 0, + DestinationType->minimumBitWidth(), + *DestinationType); + if (!Found) + return std::unexpected(Found.error()); + if (!Found->isNoRegister()) { + auto FoundType = MRI.getType(*Found); + if (!FoundType) + return std::unexpected(FoundType.error()); + if (*FoundType == *DestinationType) { + auto Replaced = replaceRegOrBuildCopy(*Destination, *Found, UpdatedDefs, + Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + } + return false; +} + +inline Expected<bool> +LegalizationArtifactCombiner::ArtifactValueFinder::tryCombineMergeLike( + GMergeLikeInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelChangeObserver &Observer) { + if (Instruction.getNumSources() == 0) + return false; + const Register FirstElement = Instruction.getSourceReg(0); + auto ElementType = Combiner.MRI.getType(FirstElement); + if (!ElementType) + return std::unexpected(ElementType.error()); + const std::uint64_t ElementSize = ElementType->minimumBitWidth(); + std::size_t FirstUnmergeIndex = 0; + auto UnmergeResult = + findUnmergeThatDefinesReg(FirstElement, ElementSize, FirstUnmergeIndex); + if (!UnmergeResult) + return std::unexpected(UnmergeResult.error()); + GUnmerge *Unmerge = *UnmergeResult; + if (Unmerge == nullptr) + return false; + + const Register Destination = Instruction.getReg(0); + const Register UnmergeSource = Unmerge->getSourceReg(); + auto DestinationType = Combiner.MRI.getType(Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto UnmergeSourceType = Combiner.MRI.getType(UnmergeSource); + if (!UnmergeSourceType) + return std::unexpected(UnmergeSourceType.error()); + const std::size_t SourceCount = Instruction.getNumSources(); + + if (*DestinationType == *UnmergeSourceType && FirstUnmergeIndex == 0) { + auto Sequence = isSequenceFromUnmerge( + Instruction, 0, Unmerge, 0, SourceCount, ElementSize, + DestinationType->isVector()); + if (!Sequence) + return std::unexpected(Sequence.error()); + if (!*Sequence) + return false; + auto Replaced = Combiner.replaceRegOrBuildCopy( + Destination, UnmergeSource, UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + DeadInstructions.push_back(&Instruction); + return true; + } + + const bool MatchingDomains = + (!DestinationType->isVector() && !UnmergeSourceType->isVector()) || + (DestinationType->isVector() && UnmergeSourceType->isVector() && + DestinationType->scalarElementType() == + UnmergeSourceType->scalarElementType()); + if (MatchingDomains && FirstUnmergeIndex % SourceCount == 0 && + UnmergeSourceType->minimumBitWidth() % + DestinationType->minimumBitWidth() == + 0) { + auto Sequence = isSequenceFromUnmerge( + Instruction, 0, Unmerge, FirstUnmergeIndex, SourceCount, ElementSize, + false); + if (!Sequence) + return std::unexpected(Sequence.error()); + if (*Sequence) { + auto NewUnmerge = Combiner.buildOrFindUnmerge(*DestinationType, + UnmergeSource); + if (!NewUnmerge) + return std::unexpected(NewUnmerge.error()); + const std::size_t DestinationIndex = static_cast<std::size_t>( + (FirstUnmergeIndex * ElementSize) / + DestinationType->minimumBitWidth()); + if (DestinationIndex >= NewUnmerge->size()) + return false; + auto Replaced = Combiner.replaceRegOrBuildCopy( + Destination, (*NewUnmerge)[DestinationIndex], UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + DeadInstructions.push_back(&Instruction); + return true; + } + } + + if (DestinationType->isVector() == UnmergeSourceType->isVector() && + DestinationType->minimumBitWidth() % + UnmergeSourceType->minimumBitWidth() == + 0) { + const std::size_t UnmergeElementCount = Unmerge->getNumDefs(); + if (UnmergeElementCount == 0 || SourceCount % UnmergeElementCount != 0) + return false; + std::vector<Register> MergeSources; + for (std::size_t Start = 0; Start < SourceCount; + Start += UnmergeElementCount) { + std::size_t SourceDefIndex = 0; + auto SourceUnmerge = findUnmergeThatDefinesReg( + Instruction.getSourceReg(Start), ElementSize, SourceDefIndex); + if (!SourceUnmerge) + return std::unexpected(SourceUnmerge.error()); + if (*SourceUnmerge == nullptr || + (*SourceUnmerge)->getNumDefs() != UnmergeElementCount || + SourceDefIndex != 0) + return false; + auto Sequence = isSequenceFromUnmerge( + Instruction, Start, *SourceUnmerge, 0, UnmergeElementCount, + ElementSize, false); + if (!Sequence) + return std::unexpected(Sequence.error()); + if (!*Sequence) + return false; + MergeSources.push_back((*SourceUnmerge)->getSourceReg()); + } + auto Built = Combiner.buildMergeLike(Destination, MergeSources); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(Destination); + DeadInstructions.push_back(&Instruction); + return true; + } + return false; +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineMergeLike( + MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + if (!GMergeLikeInstr::classof(&Instruction)) + return false; + ArtifactValueFinder Finder(*this); + return Finder.tryCombineMergeLike( + castGenericMachineInstr<GMergeLikeInstr>(Instruction), DeadInstructions, + UpdatedDefs, Observer); +} + +inline Expected<bool> LegalizationArtifactCombiner::tryFoldUnmergeCast( + MachineInstr &Instruction, MachineInstr &Cast, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs) { + if (!isArtifactCast(Cast.opcode()) || + Cast.opcode() != opcode(GenericOpcode::G_TRUNC)) + return false; + const auto CastSource = registerOperand(Cast, 1); + if (!CastSource) + return false; + auto CastSourceType = MRI.getType(*CastSource); + if (!CastSourceType) + return std::unexpected(CastSourceType.error()); + const std::size_t OriginalDefCount = Instruction.operandCount() - 1; + if (OriginalDefCount == 0) + return false; + const auto FirstDestination = registerOperand(Instruction, 0); + if (!FirstDestination) + return false; + auto DestinationType = MRI.getType(*FirstDestination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + if (DestinationType->isVector() || !CastSourceType->isCategoryNeutralScalar() || + CastSourceType->minimumBitWidth() % + DestinationType->minimumBitWidth() != + 0) + return false; + auto Unsupported = isInstUnsupported(LegalizationQuery( + opcode(GenericOpcode::G_UNMERGE_VALUES), + {*DestinationType, *CastSourceType})); + if (!Unsupported) + return std::unexpected(Unsupported.error()); + if (*Unsupported) + return false; + const std::size_t NewDefCount = static_cast<std::size_t>( + CastSourceType->minimumBitWidth() / + DestinationType->minimumBitWidth()); + std::vector<Register> Destinations; + Destinations.reserve(NewDefCount); + for (std::size_t Index = 0; Index < NewDefCount; ++Index) { + if (Index < OriginalDefCount) { + const auto Destination = registerOperand(Instruction, Index); + if (!Destination) + return false; + Destinations.push_back(*Destination); + } else { + auto Destination = MRI.createVirtualRegister(*DestinationType); + if (!Destination) + return std::unexpected(Destination.error()); + Destinations.push_back(*Destination); + } + } + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + auto Built = Builder.buildUnmergeValues(Destinations, *CastSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.insert(UpdatedDefs.end(), Destinations.begin(), + Destinations.begin() + + static_cast<std::ptrdiff_t>(OriginalDefCount)); + markInstAndDefDead(Instruction, Cast, DeadInstructions); + return true; +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineUnmergeValues( + MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + if (!GUnmerge::classof(&Instruction)) + return false; + auto &Unmerge = castGenericMachineInstr<GUnmerge>(Instruction); + if (Unmerge.getNumDefs() == 0) + return false; + const Register Source = Unmerge.getSourceReg(); + auto Definition = definitionIgnoringCopies(Source); + if (!Definition) + return std::unexpected(Definition.error()); + MachineInstr *SourceDefinition = Definition->first; + if (SourceDefinition == nullptr) + return false; + const std::size_t SourceDefIndex = getDefIndex(*SourceDefinition, + Definition->second); + auto OperandType = MRI.getType(Source); + if (!OperandType) + return std::unexpected(OperandType.error()); + auto DestinationType = MRI.getType(Unmerge.getReg(0)); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + + ArtifactValueFinder Finder(*this); + auto AllCombined = Finder.tryCombineUnmergeDefs(Unmerge, Observer, + UpdatedDefs); + if (!AllCombined) + return std::unexpected(AllCombined.error()); + if (*AllCombined) { + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions, + SourceDefIndex); + return true; + } + + if (GUnmerge::classof(SourceDefinition)) { + auto &SourceUnmerge = castGenericMachineInstr<GUnmerge>(*SourceDefinition); + const Register OriginalSource = SourceUnmerge.getSourceReg(); + auto OriginalSourceType = MRI.getType(OriginalSource); + if (!OriginalSourceType) + return std::unexpected(OriginalSourceType.error()); + auto NewUnmerge = buildOrFindUnmerge(*DestinationType, OriginalSource); + if (!NewUnmerge) + return std::unexpected(NewUnmerge.error()); + const std::size_t BaseIndex = SourceDefIndex * Unmerge.getNumDefs(); + if (BaseIndex + Unmerge.getNumDefs() > NewUnmerge->size()) + return false; + for (std::size_t Index = 0; Index < Unmerge.getNumDefs(); ++Index) { + auto Replaced = replaceRegOrBuildCopy( + Unmerge.getReg(Index), (*NewUnmerge)[BaseIndex + Index], UpdatedDefs, + Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions, + SourceDefIndex); + return true; + } + + MachineInstr *Merge = SourceDefinition; + std::optional<Opcode> ConvertOpcode; + if (isArtifactCast(SourceDefinition->opcode())) { + ConvertOpcode = SourceDefinition->opcode(); + const auto CastSource = registerOperand(*SourceDefinition, 1); + if (!CastSource) + return false; + auto CastDefinition = definitionIgnoringCopies(*CastSource); + if (!CastDefinition) + return std::unexpected(CastDefinition.error()); + Merge = CastDefinition->first; + } + if (Merge == nullptr || + !canFoldMergeOpcode(Merge->opcode(), ConvertOpcode, *OperandType, + *DestinationType)) + return tryFoldUnmergeCast(Instruction, *SourceDefinition, + DeadInstructions, UpdatedDefs); + + const std::size_t MergeSourceCount = Merge->operandCount() - 1; + const std::size_t DefCount = Unmerge.getNumDefs(); + if (MergeSourceCount < DefCount) { + if (DefCount % MergeSourceCount != 0) + return false; + const std::size_t NewDefCount = DefCount / MergeSourceCount; + for (std::size_t SourceIndex = 0; SourceIndex < MergeSourceCount; + ++SourceIndex) { + std::vector<Register> Destinations; + Destinations.reserve(NewDefCount); + for (std::size_t Index = 0; Index < NewDefCount; ++Index) + Destinations.push_back( + Unmerge.getReg(SourceIndex * NewDefCount + Index)); + Register SplitSource = *registerOperand(*Merge, SourceIndex + 1); + if (ConvertOpcode) { + auto ConvertedType = divideType(*OperandType, MergeSourceCount); + if (!ConvertedType) + return std::unexpected(ConvertedType.error()); + auto Converted = MRI.createVirtualRegister(*ConvertedType); + if (!Converted) + return std::unexpected(Converted.error()); + auto Built = buildUnary( + static_cast<GenericOpcode>(ConvertOpcode->value()), *Converted, + SplitSource); + if (!Built) + return std::unexpected(Built.error()); + SplitSource = *Converted; + } + auto Built = Builder.buildUnmergeValues(Destinations, SplitSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.insert(UpdatedDefs.end(), Destinations.begin(), + Destinations.end()); + } + } else if (MergeSourceCount > DefCount) { + if (ConvertOpcode || MergeSourceCount % DefCount != 0) + return false; + const std::size_t SourcesPerDef = MergeSourceCount / DefCount; + for (std::size_t DefIndex = 0; DefIndex < DefCount; ++DefIndex) { + std::vector<Register> Sources; + Sources.reserve(SourcesPerDef); + for (std::size_t Index = 0; Index < SourcesPerDef; ++Index) { + const auto SourceReg = registerOperand( + *Merge, 1 + DefIndex * SourcesPerDef + Index); + if (!SourceReg) + return false; + Sources.push_back(*SourceReg); + } + auto Built = buildMergeLike(Unmerge.getReg(DefIndex), Sources); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(Unmerge.getReg(DefIndex)); + } + } else { + const auto FirstMergeSource = registerOperand(*Merge, 1); + if (!FirstMergeSource) + return false; + auto MergeSourceType = MRI.getType(*FirstMergeSource); + if (!MergeSourceType) + return std::unexpected(MergeSourceType.error()); + if (!ConvertOpcode && *DestinationType != *MergeSourceType) { + if (DestinationType->isPointer()) + ConvertOpcode = opcode(GenericOpcode::G_INTTOPTR); + else if (MergeSourceType->isPointer()) + ConvertOpcode = opcode(GenericOpcode::G_PTRTOINT); + else + ConvertOpcode = opcode(GenericOpcode::G_BITCAST); + } + for (std::size_t Index = 0; Index < DefCount; ++Index) { + const auto MergeSource = registerOperand(*Merge, Index + 1); + if (!MergeSource) + return false; + if (ConvertOpcode) { + if (!MRI.useEmpty(Unmerge.getReg(Index))) { + auto Built = buildUnary( + static_cast<GenericOpcode>(ConvertOpcode->value()), + Unmerge.getReg(Index), *MergeSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(Unmerge.getReg(Index)); + } + } else { + auto Replaced = replaceRegOrBuildCopy( + Unmerge.getReg(Index), *MergeSource, UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + } + } + markInstAndDefDead(Instruction, *Merge, DeadInstructions); + return true; +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineExtract( + MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs) { + const auto Destination = registerOperand(Instruction, 0); + const auto OriginalSource = registerOperand(Instruction, 1); + const auto OffsetValue = immediateOperand(Instruction, 2); + if (!Destination || !OriginalSource || !OffsetValue || *OffsetValue < 0) + return false; + const Register Source = lookThroughCopyInstrs(*OriginalSource); + MachineInstr *Merge = MRI.getVRegDef(Source); + if (Merge == nullptr) + return false; + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + if (hasOpcode(Merge, GenericOpcode::G_IMPLICIT_DEF)) { + auto Built = Builder.buildImplicitDef(*Destination); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *Merge, DeadInstructions); + return true; + } + if (!GMergeLikeInstr::classof(Merge)) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = MRI.getType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + const std::size_t SourceCount = Merge->operandCount() - 1; + if (SourceCount == 0) + return false; + const std::uint64_t MergeSourceSize = + SourceType->minimumBitWidth() / SourceCount; + const std::uint64_t Offset = static_cast<std::uint64_t>(*OffsetValue); + const std::size_t FirstSource = + static_cast<std::size_t>(Offset / MergeSourceSize); + const std::size_t LastSource = static_cast<std::size_t>( + (Offset + DestinationType->minimumBitWidth() - 1) / MergeSourceSize); + if (FirstSource != LastSource || FirstSource >= SourceCount) + return false; + const auto MergeSource = registerOperand(*Merge, FirstSource + 1); + if (!MergeSource) + return false; + auto Built = Builder.buildExtract( + *Destination, *MergeSource, + Offset - FirstSource * MergeSourceSize); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *Merge, DeadInstructions); + return true; +} + +inline Expected<Register> +LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromDef( + Register DefReg, std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType) { + CurrentBest = {}; + Visited.clear(); + auto Found = findValueFromDefImpl(DefReg, StartBit, Size, DestinationType); + if (!Found) + return std::unexpected(Found.error()); + return *Found == DefReg ? Register{} : *Found; +} + +inline Expected<Register> +LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromConcat( + GConcatVectors &Instruction, std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType) { + if (Instruction.getNumSources() == 0) + return CurrentBest; + const Register FirstSource = Instruction.getSourceReg(0); + auto FirstType = Combiner.MRI.getType(FirstSource); + if (!FirstType) + return std::unexpected(FirstType.error()); + const std::uint64_t SourceSize = FirstType->minimumBitWidth(); + if (SourceSize == 0) + return CurrentBest; + const std::size_t SourceIndex = static_cast<std::size_t>(StartBit / SourceSize); + const std::uint64_t SourceOffset = StartBit % SourceSize; + if (SourceIndex >= Instruction.getNumSources() || + SourceOffset + Size > SourceSize) + return CurrentBest; + const Register Source = Instruction.getSourceReg(SourceIndex); + if (SourceOffset == 0 && Size == SourceSize) + CurrentBest = Source; + return findValueFromDefImpl(Source, SourceOffset, Size, DestinationType); +} + +inline Expected<Register> +LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromBuildVector( + GBuildVector &Instruction, std::uint64_t StartBit, std::uint64_t Size) { + if (Instruction.getNumSources() == 0) + return CurrentBest; + const Register FirstSource = Instruction.getSourceReg(0); + auto FirstType = Combiner.MRI.getType(FirstSource); + if (!FirstType) + return std::unexpected(FirstType.error()); + const std::uint64_t SourceSize = FirstType->minimumBitWidth(); + if (SourceSize == 0 || StartBit % SourceSize != 0 || Size < SourceSize || + Size % SourceSize != 0) + return CurrentBest; + const std::size_t FirstIndex = static_cast<std::size_t>(StartBit / SourceSize); + const std::size_t SourceCount = static_cast<std::size_t>(Size / SourceSize); + if (FirstIndex + SourceCount > Instruction.getNumSources()) + return CurrentBest; + if (SourceCount == 1) + return Instruction.getSourceReg(FirstIndex); + if (FirstIndex == 0 && SourceCount == Instruction.getNumSources()) + return Instruction.getReg(0); + + auto VectorType = LowLevelType::makeFixedVector(SourceCount, *FirstType); + if (!VectorType) + return std::unexpected(VectorType.error()); + auto Legal = Combiner.isInstLegal(LegalizationQuery( + opcode(GenericOpcode::G_BUILD_VECTOR), {*VectorType, *FirstType})); + if (!Legal) + return std::unexpected(Legal.error()); + if (!*Legal) + return CurrentBest; + + std::vector<Register> Sources; + Sources.reserve(SourceCount); + for (std::size_t Index = FirstIndex; Index < FirstIndex + SourceCount; + ++Index) + Sources.push_back(Instruction.getSourceReg(Index)); + auto Destination = Combiner.MRI.createVirtualRegister(*VectorType); + if (!Destination) + return std::unexpected(Destination.error()); + auto Built = Combiner.buildMergeLike(*Destination, Sources); + if (!Built) + return std::unexpected(Built.error()); + return *Destination; +} + +inline Expected<Register> +LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromInsert( + MachineInstr &Instruction, std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType) { + const auto Container = registerOperand(Instruction, 1); + const auto Inserted = registerOperand(Instruction, 2); + const auto InsertOffset = immediateOperand(Instruction, 3); + if (!Container || !Inserted || !InsertOffset || *InsertOffset < 0) + return CurrentBest; + auto InsertedType = Combiner.MRI.getType(*Inserted); + if (!InsertedType) + return std::unexpected(InsertedType.error()); + const std::uint64_t Offset = static_cast<std::uint64_t>(*InsertOffset); + const std::uint64_t InsertedEnd = Offset + InsertedType->minimumBitWidth(); + const std::uint64_t End = StartBit + Size; + if (End <= Offset || InsertedEnd <= StartBit) + return findValueFromDefImpl(*Container, StartBit, Size, DestinationType); + if (Offset <= StartBit && End <= InsertedEnd) { + const std::uint64_t NewStart = StartBit - Offset; + if (NewStart == 0 && Size == InsertedType->minimumBitWidth()) + CurrentBest = *Inserted; + return findValueFromDefImpl(*Inserted, NewStart, Size, DestinationType); + } + return Register{}; +} + +inline Expected<Register> +LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromExtension( + MachineInstr &Instruction, std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType) { + const auto Source = registerOperand(Instruction, 1); + if (!Source) + return CurrentBest; + auto SourceType = Combiner.MRI.getType(*Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + if (!SourceType->isCategoryNeutralScalar() || + StartBit + Size > SourceType->minimumBitWidth()) + return CurrentBest; + if (StartBit == 0 && SourceType->minimumBitWidth() == Size) + CurrentBest = *Source; + return findValueFromDefImpl(*Source, StartBit, Size, DestinationType); +} + +inline Expected<Register> +LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromTrunc( + MachineInstr &Instruction, std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType) { + const auto Source = registerOperand(Instruction, 1); + if (!Source) + return CurrentBest; + auto SourceType = Combiner.MRI.getType(*Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + if (!SourceType->isCategoryNeutralScalar()) + return CurrentBest; + return findValueFromDefImpl(*Source, StartBit, Size, DestinationType); +} + +inline Expected<Register> +LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromDefImpl( + Register DefReg, std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType) { + if (!DefReg.isVirtual() || !Visited.insert(DefReg).second) + return CurrentBest; + auto Definition = Combiner.definitionIgnoringCopies(DefReg); + if (!Definition) + return std::unexpected(Definition.error()); + MachineInstr *Instruction = Definition->first; + DefReg = Definition->second; + if (Instruction == nullptr) + return CurrentBest; + + switch (static_cast<GenericOpcode>(Instruction->opcode().value())) { + case GenericOpcode::G_CONCAT_VECTORS: + return findValueFromConcat( + castGenericMachineInstr<GConcatVectors>(*Instruction), StartBit, Size, + DestinationType); + case GenericOpcode::G_UNMERGE_VALUES: { + auto DefType = Combiner.MRI.getType(DefReg); + if (!DefType) + return std::unexpected(DefType.error()); + const std::size_t DefIndex = getDefIndex(*Instruction, DefReg); + const auto Source = registerOperand(*Instruction, + Instruction->operandCount() - 1); + if (!Source) + return CurrentBest; + auto Found = findValueFromDefImpl( + *Source, StartBit + DefIndex * DefType->minimumBitWidth(), Size, + DestinationType); + if (!Found) + return std::unexpected(Found.error()); + if (!Found->isNoRegister()) + return *Found; + if (StartBit == 0 && Size == DefType->minimumBitWidth()) + return DefReg; + return CurrentBest; + } + case GenericOpcode::G_BUILD_VECTOR: + return findValueFromBuildVector( + castGenericMachineInstr<GBuildVector>(*Instruction), StartBit, Size); + case GenericOpcode::G_INSERT: + return findValueFromInsert(*Instruction, StartBit, Size, DestinationType); + case GenericOpcode::G_TRUNC: + return findValueFromTrunc(*Instruction, StartBit, Size, DestinationType); + case GenericOpcode::G_SEXT: + case GenericOpcode::G_ZEXT: + case GenericOpcode::G_ANYEXT: + return findValueFromExtension(*Instruction, StartBit, Size, + DestinationType); + case GenericOpcode::G_IMPLICIT_DEF: { + auto Type = Combiner.MRI.getType(DefReg); + if (!Type) + return std::unexpected(Type.error()); + if (*Type == DestinationType) + return DefReg; + auto Undef = Combiner.MRI.createVirtualRegister(DestinationType); + if (!Undef) + return std::unexpected(Undef.error()); + auto Built = Combiner.Builder.buildImplicitDef(*Undef); + if (!Built) + return std::unexpected(Built.error()); + return *Undef; + } + default: + return CurrentBest; + } +} + +inline Expected<bool> +LegalizationArtifactCombiner::ArtifactValueFinder::tryCombineUnmergeDefs( + GUnmerge &Instruction, GISelChangeObserver &Observer, + std::vector<Register> &UpdatedDefs) { + if (Instruction.getNumDefs() == 0) + return false; + auto DestinationType = Combiner.MRI.getType(Instruction.getReg(0)); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + bool AllDead = true; + for (std::size_t Index = 0; Index < Instruction.getNumDefs(); ++Index) { + const Register Def = Instruction.getReg(Index); + if (Combiner.MRI.useEmpty(Def)) + continue; + auto Found = findValueFromDef(Def, 0, DestinationType->minimumBitWidth(), + *DestinationType); + if (!Found) + return std::unexpected(Found.error()); + if (Found->isNoRegister()) { + AllDead = false; + continue; + } + auto FoundType = Combiner.MRI.getType(*Found); + if (!FoundType) + return std::unexpected(FoundType.error()); + if (*FoundType != *DestinationType) { + AllDead = false; + continue; + } + auto Replaced = Combiner.replaceRegOrBuildCopy(Def, *Found, UpdatedDefs, + Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + Observer.changingInstr(Instruction); + auto Restored = Instruction.operandAt(Index)->setRegister(Def); + if (!Restored) + return std::unexpected(Restored.error()); + Observer.changedInstr(Instruction); + } + return AllDead; +} + +inline Expected<GUnmerge *> +LegalizationArtifactCombiner::ArtifactValueFinder::findUnmergeThatDefinesReg( + Register Reg, std::uint64_t Size, std::size_t &DefOperandIndex) { + auto Type = Combiner.MRI.getType(Reg); + if (!Type) + return std::unexpected(Type.error()); + CurrentBest = {}; + Visited.clear(); + auto Found = findValueFromDefImpl(Reg, 0, Size, *Type); + if (!Found) + return std::unexpected(Found.error()); + if (Found->isNoRegister()) + return nullptr; + auto Definition = Combiner.definitionIgnoringCopies(*Found); + if (!Definition) + return std::unexpected(Definition.error()); + if (!GUnmerge::classof(Definition->first)) + return nullptr; + DefOperandIndex = getDefIndex(*Definition->first, Definition->second); + return &castGenericMachineInstr<GUnmerge>(*Definition->first); +} + +inline Expected<bool> +LegalizationArtifactCombiner::ArtifactValueFinder::isSequenceFromUnmerge( + GMergeLikeInstr &Instruction, std::size_t MergeStartIndex, + GUnmerge *Unmerge, std::size_t UnmergeStartIndex, + std::size_t ElementCount, std::uint64_t ElementSize, bool AllowUndef) { + for (std::size_t Index = 0; Index < ElementCount; ++Index) { + const Register Source = Instruction.getSourceReg(MergeStartIndex + Index); + std::size_t SourceDefIndex = 0; + auto SourceUnmerge = + findUnmergeThatDefinesReg(Source, ElementSize, SourceDefIndex); + if (!SourceUnmerge) + return std::unexpected(SourceUnmerge.error()); + if (*SourceUnmerge == Unmerge) { + if (SourceDefIndex != UnmergeStartIndex + Index) + return false; + continue; + } + MachineInstr *Definition = Combiner.MRI.getVRegDef(Source); + if (!AllowUndef || + !hasOpcode(Definition, GenericOpcode::G_IMPLICIT_DEF)) + return false; + } + return true; +} + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_LEGALIZATIONARTIFACTCOMBINER_H |
