diff options
Diffstat (limited to 'backend/lib/CodeGen/MachineIRBuilder.cpp')
| -rw-r--r-- | backend/lib/CodeGen/MachineIRBuilder.cpp | 1067 |
1 files changed, 1067 insertions, 0 deletions
diff --git a/backend/lib/CodeGen/MachineIRBuilder.cpp b/backend/lib/CodeGen/MachineIRBuilder.cpp new file mode 100644 index 0000000..d2ac7cc --- /dev/null +++ b/backend/lib/CodeGen/MachineIRBuilder.cpp @@ -0,0 +1,1067 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/MachineIRBuilder.h" + +#include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineFrameInfo.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineMemOperand.h" +#include "rcc/CodeGen/MachineRegisterInfo.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <optional> +#include <string> +#include <utility> +#include <vector> + +namespace rcc { + +Register MachineIRBuilderResult::reg() const noexcept { + assert(ResultKind == Kind::Register && "machine IR builder result is not a register"); + return Reg; +} + +LowLevelType MachineIRBuilderResult::type() const noexcept { + assert(ResultKind == Kind::Type && "machine IR builder result is not a type"); + return Type; +} + +Expected<LowLevelType> MachineIRBuilderResult::type(const MachineRegisterInfo &MRI) const { + if (ResultKind == Kind::Type) + return Type; + return MRI.getType(Reg); +} + +Expected<void> MachineIRBuilderResult::addDef(MachineRegisterInfo &MRI, + MachineInstrBuilder &Builder) const { + Register Definition = Reg; + if (ResultKind == Kind::Type) { + auto Created = MRI.createVirtualRegister(Type); + if (!Created) + return std::unexpected(Created.error()); + Definition = *Created; + } + return Builder.addDef(Definition); +} + +MachineInstrBuilder::MachineInstrBuilder(MachineFunction &Function, const TargetDescription &Target, + const InstructionDescriptor &Descriptor, + MachineBasicBlock &InsertBlock, + MachineInstr *InsertBefore) noexcept + : Function(&Function), Target(&Target), Descriptor(&Descriptor), InsertBlock(&InsertBlock), + InsertBefore(InsertBefore) {} + +MachineInstrBuilder::MachineInstrBuilder(MachineInstrBuilder &&Other) noexcept + : Function(Other.Function), Target(Other.Target), Descriptor(Other.Descriptor), + InsertBlock(Other.InsertBlock), InsertBefore(Other.InsertBefore), + ExplicitOperands(std::move(Other.ExplicitOperands)), + ExtraImplicitOperands(std::move(Other.ExtraImplicitOperands)), + RequestedTies(std::move(Other.RequestedTies)), MemOperands(std::move(Other.MemOperands)), + Flags(Other.Flags), Location(std::move(Other.Location)), + DebugLocation(std::move(Other.DebugLocation)), Origin(Other.Origin), IsBuilt(Other.IsBuilt) { + Other.Function = nullptr; + Other.Target = nullptr; + Other.Descriptor = nullptr; + Other.InsertBlock = nullptr; + Other.InsertBefore = nullptr; + Other.IsBuilt = true; +} + +MachineInstrBuilder &MachineInstrBuilder::operator=(MachineInstrBuilder &&Other) noexcept { + if (this == &Other) + return *this; + + Function = Other.Function; + Target = Other.Target; + Descriptor = Other.Descriptor; + InsertBlock = Other.InsertBlock; + InsertBefore = Other.InsertBefore; + ExplicitOperands = std::move(Other.ExplicitOperands); + ExtraImplicitOperands = std::move(Other.ExtraImplicitOperands); + RequestedTies = std::move(Other.RequestedTies); + MemOperands = std::move(Other.MemOperands); + Flags = Other.Flags; + Location = std::move(Other.Location); + DebugLocation = std::move(Other.DebugLocation); + Origin = Other.Origin; + IsBuilt = Other.IsBuilt; + + Other.Function = nullptr; + Other.Target = nullptr; + Other.Descriptor = nullptr; + Other.InsertBlock = nullptr; + Other.InsertBefore = nullptr; + Other.IsBuilt = true; + return *this; +} + +Expected<void> MachineInstrBuilder::ensurePending() const { + if (IsBuilt) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machine instruction builder was already consumed"); + } + return {}; +} + +Expected<void> MachineInstrBuilder::addOperand(MachineOperand &&Operand) { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + if (Operand.parent() != nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder operand already belongs to an instruction"); + } + if (Operand.isImplicit()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "implicit register requires addImplicitOperand"); + } + ExplicitOperands.push_back(std::move(Operand)); + return {}; +} + +Expected<void> MachineInstrBuilder::addImplicitOperand(MachineOperand &&Operand) { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + if (Operand.parent() != nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder operand already belongs to an instruction"); + } + if (!Operand.isRegister() || !Operand.isImplicit()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "dynamic implicit operand must be an implicit register"); + } + ExtraImplicitOperands.push_back(std::move(Operand)); + return {}; +} + +Expected<void> MachineInstrBuilder::appendRegister(Register Reg, RegisterOperandFlags RegisterFlags, + SubRegIndex SubReg, std::uint32_t TargetFlags, + bool IsImplicit) { + auto Operand = MachineOperand::registerOperand(Reg, RegisterFlags, SubReg, TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + if (IsImplicit) + return addImplicitOperand(std::move(*Operand)); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addDef(Register Reg, RegisterOperandFlags AdditionalFlags, + SubRegIndex SubReg, std::uint32_t TargetFlags) { + AdditionalFlags.set(RegisterOperandFlag::Define); + return appendRegister(Reg, AdditionalFlags, SubReg, TargetFlags, false); +} + +Expected<void> MachineInstrBuilder::addUse(Register Reg, RegisterOperandFlags AdditionalFlags, + SubRegIndex SubReg, std::uint32_t TargetFlags) { + AdditionalFlags.set(RegisterOperandFlag::Use); + return appendRegister(Reg, AdditionalFlags, SubReg, TargetFlags, false); +} + +Expected<void> MachineInstrBuilder::addDefUse(Register Reg, RegisterOperandFlags AdditionalFlags, + SubRegIndex SubReg, std::uint32_t TargetFlags) { + AdditionalFlags.set(RegisterOperandFlag::Define); + AdditionalFlags.set(RegisterOperandFlag::Use); + AdditionalFlags.set(RegisterOperandFlag::InternalRead); + return appendRegister(Reg, AdditionalFlags, SubReg, TargetFlags, false); +} + +Expected<void> MachineInstrBuilder::addImplicitDef(Register Reg, + RegisterOperandFlags AdditionalFlags, + SubRegIndex SubReg, std::uint32_t TargetFlags) { + AdditionalFlags.set(RegisterOperandFlag::Define); + AdditionalFlags.set(RegisterOperandFlag::Implicit); + return appendRegister(Reg, AdditionalFlags, SubReg, TargetFlags, true); +} + +Expected<void> MachineInstrBuilder::addImplicitUse(Register Reg, + RegisterOperandFlags AdditionalFlags, + SubRegIndex SubReg, std::uint32_t TargetFlags) { + AdditionalFlags.set(RegisterOperandFlag::Use); + AdditionalFlags.set(RegisterOperandFlag::Implicit); + return appendRegister(Reg, AdditionalFlags, SubReg, TargetFlags, true); +} + +Expected<void> MachineInstrBuilder::addImmediate(std::int64_t Value, std::uint32_t TargetFlags) { + return addOperand(MachineOperand::immediate(Value, TargetFlags)); +} + +Expected<void> MachineInstrBuilder::addIntegerImmediate(APInt Value, std::uint32_t TargetFlags) { + return addOperand(MachineOperand::integerImmediate(std::move(Value), TargetFlags)); +} + +Expected<void> MachineInstrBuilder::addFloatingImmediate(APFloat Bits, std::uint32_t TargetFlags) { + return addOperand(MachineOperand::floatingImmediate(Bits, TargetFlags)); +} + +Expected<void> MachineInstrBuilder::addBasicBlock(MachineBasicBlock &Block, + std::uint32_t TargetFlags) { + return addOperand(MachineOperand::basicBlock(Block, TargetFlags)); +} + +Expected<void> MachineInstrBuilder::addFrameIndex(FrameIndex Index, std::int64_t Offset, + std::uint32_t TargetFlags) { + return addOperand(MachineOperand::frameIndex(Index, Offset, TargetFlags)); +} + +Expected<void> MachineInstrBuilder::addConstantPool(ConstantPoolIndex Index, std::int64_t Offset, + std::uint32_t TargetFlags) { + return addOperand(MachineOperand::constantPool(Index, Offset, TargetFlags)); +} + +Expected<void> MachineInstrBuilder::addJumpTable(JumpTableIndex Index, std::uint32_t TargetFlags) { + return addOperand(MachineOperand::jumpTable(Index, TargetFlags)); +} + +Expected<void> MachineInstrBuilder::addGlobalSymbol(std::string Name, std::int64_t Offset, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::globalSymbol(std::move(Name), Offset, TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addExternalSymbol(std::string Name, std::int64_t Offset, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::externalSymbol(std::move(Name), Offset, TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addBlockAddress(std::string FunctionName, std::string BlockName, + std::int64_t Offset, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::blockAddress(std::move(FunctionName), std::move(BlockName), Offset, + TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addRegisterMask(std::shared_ptr<const RegisterMask> Mask, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::registerMask(std::move(Mask), TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addLiveOutRegisterMask(std::shared_ptr<const RegisterMask> Mask, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::liveOutRegisterMask(std::move(Mask), TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addMetadata(MachineMetadataValue Value, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::metadata(std::move(Value), TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addTargetIndex(TargetIndex Index, std::int64_t Offset, + std::uint32_t TargetFlags) { + return addOperand(MachineOperand::targetIndex(Index, Offset, TargetFlags)); +} + +Expected<void> MachineInstrBuilder::addMCSymbol(std::string Name, std::int64_t Offset, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::mcSymbol(std::move(Name), Offset, TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addCFIInstruction(CFIInstructionIndex Index, + std::uint32_t TargetFlags) { + return addOperand(MachineOperand::cfiIndex(Index, TargetFlags)); +} + +Expected<void> MachineInstrBuilder::addIntrinsic(IntrinsicID ID, std::uint32_t TargetFlags) { + auto Operand = MachineOperand::intrinsic(ID, TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addIntegerPredicate(IntegerPredicate Predicate, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::integerPredicate(Predicate, TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addFloatingPredicate(FloatingPredicate Predicate, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::floatingPredicate(Predicate, TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addShuffleMask(std::vector<std::int32_t> Elements, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::shuffleMask(std::move(Elements), TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addDebugInstrRef(std::uint64_t InstructionNumber, + std::uint64_t OperandNumber, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::debugInstrRef(InstructionNumber, OperandNumber, TargetFlags); + if (!Operand) + return std::unexpected(Operand.error()); + return addOperand(std::move(*Operand)); +} + +Expected<void> MachineInstrBuilder::addLaneMask(LaneMask Mask, std::uint32_t TargetFlags) { + return addOperand(MachineOperand::laneMask(Mask, TargetFlags)); +} + +Expected<void> MachineInstrBuilder::tieOperands(std::size_t DefOperand, std::size_t UseOperand) { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + if (DefOperand >= ExplicitOperands.size() || UseOperand >= ExplicitOperands.size() || + DefOperand == UseOperand) { + return makeUnexpected(ErrorCode::InvalidArgument, "builder operand tie indices are invalid"); + } + + const MachineOperand &Def = ExplicitOperands[DefOperand]; + const MachineOperand &Use = ExplicitOperands[UseOperand]; + if (!Def.isRegister() || !Use.isRegister() || !Def.isDef() || Def.isUse() || !Use.isUse() || + Use.isDef()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder tie must connect a pure register def and use"); + } + + const MachineOperandTie Tie{DefOperand, UseOperand}; + if (std::ranges::find(RequestedTies, Tie) == RequestedTies.end()) + RequestedTies.push_back(Tie); + return {}; +} + +Expected<void> MachineInstrBuilder::addMemOperand(const MachineMemOperand &MemOperand) { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + if (!Function->ownsMemOperand(MemOperand)) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder memory operand belongs to another function"); + } + MemOperands.push_back(&MemOperand); + return {}; +} + +Expected<void> MachineInstrBuilder::setFlags(MachineInstrFlags NewFlags) { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + Flags = NewFlags; + return {}; +} + +Expected<void> MachineInstrBuilder::setSourceLocation(SourceLocation NewLocation) { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + if (!NewLocation.isValid()) { + return makeUnexpected(ErrorCode::InvalidArgument, "builder source location is incomplete"); + } + Location = std::move(NewLocation); + return {}; +} + +Expected<void> MachineInstrBuilder::clearSourceLocation() { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + Location.reset(); + return {}; +} + +Expected<void> MachineInstrBuilder::setDebugLocation(MachineDebugLocation NewLocation) { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + if (!NewLocation.isValid()) { + return makeUnexpected(ErrorCode::InvalidArgument, "builder debug location is invalid"); + } + DebugLocation = std::move(NewLocation); + return {}; +} + +Expected<void> MachineInstrBuilder::clearDebugLocation() { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + DebugLocation.reset(); + return {}; +} + +Expected<void> MachineInstrBuilder::setOrigin(OriginID NewOrigin) { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + if (NewOrigin.value() == 0) { + return makeUnexpected(ErrorCode::InvalidArgument, "builder origin ID must be nonzero"); + } + Origin = NewOrigin; + return {}; +} + +Expected<void> MachineInstrBuilder::clearOrigin() { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + Origin.reset(); + return {}; +} + +Expected<void> MachineInstrBuilder::validateInsertionPoint() const { + if (InsertBlock == nullptr || &InsertBlock->function() != Function || !InsertBlock->isLinked()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder insertion block is not linked to its function"); + } + if (InsertBefore != nullptr && InsertBefore->parent() != InsertBlock) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder insertion cursor is no longer in its block"); + } + return {}; +} + +Expected<void> MachineInstrBuilder::validateFrameReference(FrameIndex Index) const { + const StackObject *Object = Function->frameInfo().object(Index); + if (Object == nullptr || Object->description()->IsDead) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder references an unknown or dead stack object"); + } + return {}; +} + +Expected<void> MachineInstrBuilder::validateConstantPoolReference(ConstantPoolIndex Index) const { + if (Function->constantPool().entry(Index) == nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder references an unknown constant pool entry"); + } + return {}; +} + +Expected<void> MachineInstrBuilder::validateJumpTableReference(JumpTableIndex Index) const { + if (Function->jumpTableInfo().table(Index) == nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, "builder references an unknown jump table"); + } + return {}; +} + +Expected<void> MachineInstrBuilder::validateCFIReference(CFIInstructionIndex Index) const { + if (Function->cfiInfo().instruction(Index) == nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder references an unknown CFI instruction"); + } + return {}; +} + +Expected<void> MachineInstrBuilder::validateOperandOwnership(const MachineOperand &Operand) const { + if (Operand.parent() != nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder operand already belongs to an instruction"); + } + if (const auto *Block = Operand.getIf<BasicBlockOperand>(); + Block != nullptr && (Block->Block == nullptr || &Block->Block->function() != Function || + !Block->Block->isLinked())) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder basic block operand is not linked to its function"); + } + if (const Register *Reg = Operand.getIf<Register>(); + Reg != nullptr && Reg->isVirtual() && + !Function->registerInfo().isKnownVirtualRegister(*Reg)) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder operand references an unknown virtual register"); + } + if (const auto *Frame = Operand.getIf<FrameIndexOperand>(); Frame != nullptr) { + auto FrameResult = validateFrameReference(Frame->Index); + if (!FrameResult) + return FrameResult; + } + if (const auto *Constant = Operand.getIf<ConstantPoolOperand>(); Constant != nullptr) { + auto ConstantResult = validateConstantPoolReference(Constant->Index); + if (!ConstantResult) + return ConstantResult; + } + if (const auto *JumpTable = Operand.getIf<JumpTableOperand>(); JumpTable != nullptr) { + auto JumpTableResult = validateJumpTableReference(JumpTable->Index); + if (!JumpTableResult) + return JumpTableResult; + } + if (const auto *CFI = Operand.getIf<CFIIndexOperand>(); CFI != nullptr) { + auto CFIResult = validateCFIReference(CFI->Index); + if (!CFIResult) + return CFIResult; + } + return {}; +} + +std::vector<MachineOperandTie> MachineInstrBuilder::collectOperandTies() const { + std::vector<MachineOperandTie> Result; + for (std::size_t Index = 0; Index < Descriptor->Operands.size(); ++Index) { + const OperandDescriptor &Operand = Descriptor->Operands[Index]; + if (!Operand.DefaultTiedOperand || Index > *Operand.DefaultTiedOperand) + continue; + const std::size_t Other = *Operand.DefaultTiedOperand; + const bool IndexIsDef = Operand.RegisterRole == RegisterOperandRole::Def; + Result.push_back(IndexIsDef ? MachineOperandTie{Index, Other} + : MachineOperandTie{Other, Index}); + } + for (const MachineOperandTie Tie : RequestedTies) { + if (std::ranges::find(Result, Tie) == Result.end()) + Result.push_back(Tie); + } + return Result; +} + +Expected<MachineInstr *> MachineInstrBuilder::build() { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + + auto InsertionValidation = validateInsertionPoint(); + if (!InsertionValidation) + return std::unexpected(InsertionValidation.error()); + + for (const MachineOperand &Operand : ExplicitOperands) { + auto Ownership = validateOperandOwnership(Operand); + if (!Ownership) + return std::unexpected(Ownership.error()); + } + for (const MachineOperand &Operand : ExtraImplicitOperands) { + auto Ownership = validateOperandOwnership(Operand); + if (!Ownership) + return std::unexpected(Ownership.error()); + } + for (const MachineMemOperand *MemOperand : MemOperands) { + const MemoryBase &Base = MemOperand->description().Base; + if (const auto Frame = Base.frameIndex()) { + auto FrameResult = validateFrameReference(*Frame); + if (!FrameResult) + return std::unexpected(FrameResult.error()); + } + if (const auto Constant = Base.constantPoolIndex()) { + auto ConstantResult = validateConstantPoolReference(*Constant); + if (!ConstantResult) + return std::unexpected(ConstantResult.error()); + } + if (const auto JumpTable = Base.jumpTableIndex()) { + auto JumpTableResult = validateJumpTableReference(*JumpTable); + if (!JumpTableResult) + return std::unexpected(JumpTableResult.error()); + } + } + + auto ExplicitValidation = Target->validateExplicitOperands( + Descriptor->InstructionOpcode, ExplicitOperands, Function->registerInfo()); + if (!ExplicitValidation) + return std::unexpected(ExplicitValidation.error()); + + auto DefaultImplicit = Target->createDefaultImplicitOperands(*Descriptor); + if (!DefaultImplicit) + return std::unexpected(DefaultImplicit.error()); + + std::vector<const MachineOperand *> ImplicitOperands; + ImplicitOperands.reserve(DefaultImplicit->size() + ExtraImplicitOperands.size()); + for (const MachineOperand &Operand : *DefaultImplicit) + ImplicitOperands.push_back(&Operand); + for (const MachineOperand &Operand : ExtraImplicitOperands) + ImplicitOperands.push_back(&Operand); + auto ImplicitValidation = Target->validateImplicitOperands( + Descriptor->InstructionOpcode, ImplicitOperands, Function->registerInfo()); + if (!ImplicitValidation) + return std::unexpected(ImplicitValidation.error()); + + const std::vector<MachineOperandTie> Ties = collectOperandTies(); + auto TieValidation = Target->validateOperandTies(Descriptor->InstructionOpcode, ExplicitOperands, + Ties, Function->registerInfo()); + if (!TieValidation) + return std::unexpected(TieValidation.error()); + + auto EarlyClobberValidation = + Target->validateEarlyClobber(ExplicitOperands, ImplicitOperands, Ties); + if (!EarlyClobberValidation) + return std::unexpected(EarlyClobberValidation.error()); + + auto MemoryValidation = Target->validateMemOperands(Descriptor->InstructionOpcode, MemOperands); + if (!MemoryValidation) + return std::unexpected(MemoryValidation.error()); + + auto Instruction = Function->createInstruction(Descriptor->InstructionOpcode); + if (!Instruction) + return std::unexpected(Instruction.error()); + MachineInstr *Result = *Instruction; + Result->reserveOperands(ExplicitOperands.size() + DefaultImplicit->size() + + ExtraImplicitOperands.size()); + + for (MachineOperand &Operand : ExplicitOperands) { + auto AddResult = Result->addOperand(std::move(Operand)); + if (!AddResult) + return std::unexpected(AddResult.error()); + } + for (MachineOperand &Operand : *DefaultImplicit) { + auto AddResult = Result->addOperand(std::move(Operand)); + if (!AddResult) + return std::unexpected(AddResult.error()); + } + for (MachineOperand &Operand : ExtraImplicitOperands) { + auto AddResult = Result->addOperand(std::move(Operand)); + if (!AddResult) + return std::unexpected(AddResult.error()); + } + for (const MachineOperandTie Tie : Ties) { + auto TieResult = Result->tieOperands(Tie.DefOperand, Tie.UseOperand); + if (!TieResult) + return std::unexpected(TieResult.error()); + } + for (const MachineMemOperand *MemOperand : MemOperands) { + auto AddResult = Result->addMemOperand(*MemOperand); + if (!AddResult) + return std::unexpected(AddResult.error()); + } + + Result->setFlags(Flags); + if (Location) + Result->setSourceLocation(std::move(*Location)); + if (DebugLocation) { + auto DebugLocationResult = Result->setDebugLocation(std::move(*DebugLocation)); + if (!DebugLocationResult) + return std::unexpected(DebugLocationResult.error()); + } + if (Origin) + Result->setOrigin(*Origin); + + Expected<void> InsertResult; + if (InsertBefore == nullptr) + InsertResult = InsertBlock->appendInstruction(*Result); + else + InsertResult = InsertBlock->insertInstructionBefore(*InsertBefore, *Result); + if (!InsertResult) + return std::unexpected(InsertResult.error()); + + IsBuilt = true; + return Result; +} + +Expected<void> MachineIRBuilder::validateBlock(const MachineBasicBlock &Block) const { + if (&Block.function() != Function || !Block.isLinked()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder insertion block is not linked to its function"); + } + return {}; +} + +Expected<void> MachineIRBuilder::validateInstruction(const MachineInstr &Instruction) const { + if (&Instruction.function() != Function || Instruction.parent() == nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder insertion instruction is not linked to its function"); + } + return {}; +} + +Expected<void> MachineIRBuilder::verifyInsertPoint() const { + if (InsertBlock == nullptr) + return makeUnexpected(ErrorCode::InvalidArgument, "machine IR builder has no insertion point"); + auto BlockValidation = validateBlock(*InsertBlock); + if (!BlockValidation) + return std::unexpected(BlockValidation.error()); + if (InsertBefore != nullptr && InsertBefore->parent() != InsertBlock) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machine IR builder insertion cursor is no longer in its block"); + } + return {}; +} + +Expected<void> MachineIRBuilder::setInsertPoint(MachineBasicBlock &Block) { + auto Validation = validateBlock(Block); + if (!Validation) + return std::unexpected(Validation.error()); + InsertBlock = &Block; + InsertBefore = nullptr; + return {}; +} + +Expected<void> MachineIRBuilder::setInsertPointBefore(MachineInstr &Instruction) { + auto Validation = validateInstruction(Instruction); + if (!Validation) + return std::unexpected(Validation.error()); + InsertBlock = Instruction.parent(); + InsertBefore = &Instruction; + return {}; +} + +Expected<void> MachineIRBuilder::setInsertPointAfter(MachineInstr &Instruction) { + auto Validation = validateInstruction(Instruction); + if (!Validation) + return std::unexpected(Validation.error()); + InsertBlock = Instruction.parent(); + InsertBefore = Instruction.next(); + return {}; +} + +Expected<MachineInstrBuilder> MachineIRBuilder::buildInstr(Opcode InstructionOpcode) const { + if (InsertBlock == nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, "machine IR builder has no insertion point"); + } + const InstructionDescriptor *Descriptor = Target->instructionDescriptor(InstructionOpcode); + if (Descriptor == nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machine IR builder opcode is not registered"); + } + auto BlockValidation = validateBlock(*InsertBlock); + if (!BlockValidation) + return std::unexpected(BlockValidation.error()); + if (InsertBefore != nullptr && InsertBefore->parent() != InsertBlock) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machine IR builder insertion cursor is stale"); + } + return MachineInstrBuilder(*Function, *Target, *Descriptor, *InsertBlock, InsertBefore); +} + +Expected<MachineInstr *> +MachineIRBuilder::buildInstr(Opcode InstructionOpcode, + std::initializer_list<MachineIRBuilderResult> Destinations, + std::initializer_list<Register> Sources) const { + return buildInstr( + InstructionOpcode, + std::span<const MachineIRBuilderResult>(Destinations.begin(), Destinations.size()), + std::span<const Register>(Sources.begin(), Sources.size())); +} + +Expected<MachineInstr *> +MachineIRBuilder::buildInstr(Opcode InstructionOpcode, + std::span<const MachineIRBuilderResult> Destinations, + std::span<const Register> Sources) const { + auto Pending = buildInstr(InstructionOpcode); + if (!Pending) + return std::unexpected(Pending.error()); + for (const MachineIRBuilderResult &Destination : Destinations) { + if (auto Added = Destination.addDef(Function->registerInfo(), *Pending); !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(); +} + +Expected<LowLevelType> MachineIRBuilder::registerType(Register Reg) const { + if (!Reg.isVirtual()) + return makeUnexpected(ErrorCode::InvalidArgument, + "generic builder operand is not a virtual register"); + auto Info = Function->registerInfo().virtualRegisterInfo(Reg); + if (!Info) + return std::unexpected(Info.error()); + const std::optional<LowLevelType> &Type = Info->get().Type; + if (!Type.has_value()) + return makeUnexpected(ErrorCode::InvalidArgument, + "generic builder operand has no LowLevelType"); + return Type.value(); +} + +Expected<MachineInstr *> MachineIRBuilder::buildUnary(Opcode InstructionOpcode, + MachineIRBuilderResult Destination, + Register Source) const { + const std::array Destinations{Destination}; + const std::array Sources{Source}; + return buildInstr(InstructionOpcode, std::span<const MachineIRBuilderResult>(Destinations), + std::span<const Register>(Sources)); +} + +Expected<MachineInstr *> MachineIRBuilder::buildBinary(Opcode InstructionOpcode, + MachineIRBuilderResult Destination, + Register Left, Register Right) const { + const std::array Destinations{Destination}; + const std::array Sources{Left, Right}; + return buildInstr(InstructionOpcode, std::span<const MachineIRBuilderResult>(Destinations), + std::span<const Register>(Sources)); +} + +Expected<MachineInstr *> MachineIRBuilder::buildTrunc(MachineIRBuilderResult Destination, + Register Source) const { + return buildUnary(opcode(GenericOpcode::G_TRUNC), Destination, Source); +} + +Expected<MachineInstr *> MachineIRBuilder::buildCopy(MachineIRBuilderResult Destination, + Register Source) const { + return buildUnary(opcode(GenericOpcode::COPY), Destination, Source); +} + +Expected<MachineInstr *> +MachineIRBuilder::buildImplicitDef(MachineIRBuilderResult Destination) const { + const std::array Destinations{Destination}; + return buildInstr(opcode(GenericOpcode::G_IMPLICIT_DEF), + std::span<const MachineIRBuilderResult>(Destinations), + std::span<const Register>()); +} + +Expected<MachineInstr *> MachineIRBuilder::buildAnyExt(MachineIRBuilderResult Destination, + Register Source) const { + return buildUnary(opcode(GenericOpcode::G_ANYEXT), Destination, Source); +} + +Expected<MachineInstr *> MachineIRBuilder::buildZExt(MachineIRBuilderResult Destination, + Register Source) const { + return buildUnary(opcode(GenericOpcode::G_ZEXT), Destination, Source); +} + +Expected<MachineInstr *> MachineIRBuilder::buildSExt(MachineIRBuilderResult Destination, + Register Source) const { + return buildUnary(opcode(GenericOpcode::G_SEXT), Destination, Source); +} + +GenericOpcode MachineIRBuilder::getBoolExtOp(bool IsVector, bool IsFloatingPoint) const noexcept { + switch (Target->getBooleanContents(IsVector, IsFloatingPoint)) { + case BooleanContent::ZeroOrNegativeOne: + return GenericOpcode::G_SEXT; + case BooleanContent::ZeroOrOne: + return GenericOpcode::G_ZEXT; + case BooleanContent::Undefined: + return GenericOpcode::G_ANYEXT; + } + return GenericOpcode::G_ANYEXT; +} + +Expected<MachineInstr *> MachineIRBuilder::buildUndef(MachineIRBuilderResult Destination) const { + return buildImplicitDef(Destination); +} + +Expected<MachineInstr *> MachineIRBuilder::buildExtOrTrunc(Opcode ExtensionOpcode, + MachineIRBuilderResult Destination, + Register Source) const { + auto DestinationType = Destination.type(Function->registerInfo()); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = registerType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + const std::uint64_t DestinationWidth = DestinationType->minimumBitWidth(); + const std::uint64_t SourceWidth = SourceType->minimumBitWidth(); + if (DestinationWidth > SourceWidth) + return buildUnary(ExtensionOpcode, Destination, Source); + if (DestinationWidth < SourceWidth) + return buildTrunc(Destination, Source); + return buildUnary(opcode(GenericOpcode::COPY), Destination, Source); +} + +Expected<MachineInstr *> MachineIRBuilder::buildAnyExtOrTrunc(MachineIRBuilderResult Destination, + Register Source) const { + return buildExtOrTrunc(opcode(GenericOpcode::G_ANYEXT), Destination, Source); +} + +Expected<MachineInstr *> MachineIRBuilder::buildZExtOrTrunc(MachineIRBuilderResult Destination, + Register Source) const { + return buildExtOrTrunc(opcode(GenericOpcode::G_ZEXT), Destination, Source); +} + +Expected<MachineInstr *> MachineIRBuilder::buildSExtOrTrunc(MachineIRBuilderResult Destination, + Register Source) const { + return buildExtOrTrunc(opcode(GenericOpcode::G_SEXT), Destination, Source); +} + +Expected<MachineInstr *> MachineIRBuilder::buildConstant(MachineIRBuilderResult Destination, + APInt Value) const { + auto Pending = buildInstr(opcode(GenericOpcode::G_CONSTANT)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Destination.addDef(Function->registerInfo(), *Pending); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addIntegerImmediate(std::move(Value)); !Added) + return std::unexpected(Added.error()); + return Pending->build(); +} + +Expected<MachineInstr *> MachineIRBuilder::buildConstant(MachineIRBuilderResult Destination, + std::int64_t Value) const { + auto Type = Destination.type(Function->registerInfo()); + if (!Type) + return std::unexpected(Type.error()); + auto Immediate = APInt::fromSigned(Type->scalarBitWidth(), Value); + if (!Immediate) + return std::unexpected(Immediate.error()); + return buildConstant(Destination, std::move(*Immediate)); +} + +Expected<MachineInstr *> MachineIRBuilder::buildSExtInReg(MachineIRBuilderResult Destination, + Register Source, + std::uint32_t Width) const { + auto Pending = buildInstr(opcode(GenericOpcode::G_SEXT_INREG)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Destination.addDef(Function->registerInfo(), *Pending); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Source); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addImmediate(static_cast<std::int64_t>(Width)); !Added) + return std::unexpected(Added.error()); + return Pending->build(); +} + +Expected<MachineInstr *> MachineIRBuilder::buildAnd(MachineIRBuilderResult Destination, + Register Left, Register Right) const { + return buildBinary(opcode(GenericOpcode::G_AND), Destination, Left, Right); +} + +Expected<MachineInstr *> MachineIRBuilder::buildOr(MachineIRBuilderResult Destination, + Register Left, Register Right) const { + return buildBinary(opcode(GenericOpcode::G_OR), Destination, Left, Right); +} + +Expected<MachineInstr *> MachineIRBuilder::buildXor(MachineIRBuilderResult Destination, + Register Left, Register Right) const { + return buildBinary(opcode(GenericOpcode::G_XOR), Destination, Left, Right); +} + +Expected<MachineInstr *> MachineIRBuilder::buildAdd(MachineIRBuilderResult Destination, + Register Left, Register Right) const { + return buildBinary(opcode(GenericOpcode::G_ADD), Destination, Left, Right); +} + +Expected<MachineInstr *> MachineIRBuilder::buildSub(MachineIRBuilderResult Destination, + Register Left, Register Right) const { + return buildBinary(opcode(GenericOpcode::G_SUB), Destination, Left, Right); +} + +Expected<MachineInstr *> MachineIRBuilder::buildShl(MachineIRBuilderResult Destination, + Register Left, Register Right) const { + return buildBinary(opcode(GenericOpcode::G_SHL), Destination, Left, Right); +} + +Expected<MachineInstr *> MachineIRBuilder::buildLShr(MachineIRBuilderResult Destination, + Register Left, Register Right) const { + return buildBinary(opcode(GenericOpcode::G_LSHR), Destination, Left, Right); +} + +Expected<MachineInstr *> MachineIRBuilder::buildAShr(MachineIRBuilderResult Destination, + Register Left, Register Right) const { + return buildBinary(opcode(GenericOpcode::G_ASHR), Destination, Left, Right); +} + +Expected<MachineInstr *> MachineIRBuilder::buildPtrToInt(MachineIRBuilderResult Destination, + Register Source) const { + return buildUnary(opcode(GenericOpcode::G_PTRTOINT), Destination, Source); +} + +Expected<MachineInstr *> MachineIRBuilder::buildIntToPtr(MachineIRBuilderResult Destination, + Register Source) const { + return buildUnary(opcode(GenericOpcode::G_INTTOPTR), Destination, Source); +} + +Expected<MachineInstr *> MachineIRBuilder::buildICmp(IntegerPredicate Predicate, + MachineIRBuilderResult Destination, + Register Left, Register Right) const { + auto Pending = buildInstr(opcode(GenericOpcode::G_ICMP)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Destination.addDef(Function->registerInfo(), *Pending); !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()); + return Pending->build(); +} + +Expected<MachineInstr *> +MachineIRBuilder::buildMergeLikeInstr(MachineIRBuilderResult Destination, + const std::vector<Register> &Sources) const { + auto DestinationType = Destination.type(Function->registerInfo()); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + if (!DestinationType->isVector()) + return buildMergeValues(Destination, Sources); + if (Sources.empty()) + return makeUnexpected(ErrorCode::InvalidArgument, + "merge-like instruction requires source registers"); + auto SourceType = registerType(Sources.front()); + if (!SourceType) + return std::unexpected(SourceType.error()); + const GenericOpcode MergeOpcode = + SourceType->isVector() ? GenericOpcode::G_CONCAT_VECTORS : GenericOpcode::G_BUILD_VECTOR; + const std::array Destinations{Destination}; + return buildInstr(opcode(MergeOpcode), std::span<const MachineIRBuilderResult>(Destinations), + Sources); +} + +Expected<MachineInstr *> +MachineIRBuilder::buildMergeValues(MachineIRBuilderResult Destination, + const std::vector<Register> &Sources) const { + const std::array Destinations{Destination}; + return buildInstr(opcode(GenericOpcode::G_MERGE_VALUES), + std::span<const MachineIRBuilderResult>(Destinations), Sources); +} + +Expected<MachineInstr *> +MachineIRBuilder::buildUnmergeValues(const std::vector<Register> &Destinations, + Register Source) const { + std::vector<MachineIRBuilderResult> Results; + Results.reserve(Destinations.size()); + for (Register Destination : Destinations) + Results.emplace_back(Destination); + const std::array Sources{Source}; + return buildInstr(opcode(GenericOpcode::G_UNMERGE_VALUES), Results, + std::span<const Register>(Sources)); +} + +Expected<MachineInstr *> MachineIRBuilder::buildExtract(MachineIRBuilderResult Destination, + Register Source, + std::uint64_t Offset) const { + if (Offset > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) + return makeUnexpected(ErrorCode::InvalidArgument, + "generic extract offset does not fit a machine immediate"); + auto Pending = buildInstr(opcode(GenericOpcode::G_EXTRACT)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Destination.addDef(Function->registerInfo(), *Pending); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Source); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addImmediate(static_cast<std::int64_t>(Offset)); !Added) + return std::unexpected(Added.error()); + return Pending->build(); +} + +Expected<MachineInstr *> MachineIRBuilder::buildInsert(MachineIRBuilderResult Destination, + Register Source, Register Inserted, + std::uint64_t Offset) const { + if (Offset > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) + return makeUnexpected(ErrorCode::InvalidArgument, + "generic insert offset does not fit a machine immediate"); + auto Pending = buildInstr(opcode(GenericOpcode::G_INSERT)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Destination.addDef(Function->registerInfo(), *Pending); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Source); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Inserted); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addImmediate(static_cast<std::int64_t>(Offset)); !Added) + return std::unexpected(Added.error()); + return Pending->build(); +} + +} // namespace rcc |
