diff options
Diffstat (limited to 'backend/lib/AsmPrinter')
| -rw-r--r-- | backend/lib/AsmPrinter/AsmPrinter.cpp | 1080 | ||||
| -rw-r--r-- | backend/lib/AsmPrinter/CMakeLists.txt | 58 | ||||
| -rw-r--r-- | backend/lib/AsmPrinter/MachineMCContext.cpp | 153 | ||||
| -rw-r--r-- | backend/lib/AsmPrinter/TargetMCInstLowering.cpp | 14 |
4 files changed, 1305 insertions, 0 deletions
diff --git a/backend/lib/AsmPrinter/AsmPrinter.cpp b/backend/lib/AsmPrinter/AsmPrinter.cpp new file mode 100644 index 0000000..378d7fa --- /dev/null +++ b/backend/lib/AsmPrinter/AsmPrinter.cpp @@ -0,0 +1,1080 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/AsmPrinter.h" + +#include "rcc/CodeGen/CompilationUnit.h" +#include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/GlobalObject.h" +#include "rcc/CodeGen/InstructionDescriptor.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineCFIInfo.h" +#include "rcc/CodeGen/MachineConstantPool.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineJumpTableInfo.h" +#include "rcc/CodeGen/MachineMCContext.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/MachineVerifier.h" +#include "rcc/CodeGen/TargetConstant.h" +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/CodeGen/TargetLoweringObjectFile.h" +#include "rcc/CodeGen/TargetMCInstLowering.h" +#include "rcc/IR/Constants.h" +#include "rcc/MC/MCCFI.h" +#include "rcc/MC/MCContext.h" +#include "rcc/MC/MCExpr.h" +#include "rcc/MC/MCSection.h" +#include "rcc/MC/MCStreamer.h" +#include "rcc/MC/MCSymbol.h" + +#include <algorithm> +#include <bit> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <ranges> +#include <span> +#include <string> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { +namespace { + +[[nodiscard]] Expected<mc::MCExprRef> symbolExpression(mc::MCSymbolRef Symbol) { + auto Expression = mc::MCExpr::symbol(std::move(Symbol)); + if (!Expression) + return std::unexpected(Expression.error()); + return *Expression; +} + +[[nodiscard]] Expected<mc::MCExprRef> addConstant(mc::MCExprRef Expression, std::int64_t Addend) { + if (Addend == 0) + return Expression; + auto Result = mc::MCExpr::binary(mc::MCBinaryOperator::Add, std::move(Expression), + mc::MCExpr::constant(Addend)); + if (!Result) + return std::unexpected(Result.error()); + return *Result; +} + +[[nodiscard]] Expected<mc::MCExprRef> +symbolDifference(mc::MCSymbolRef Minuend, mc::MCSymbolRef Subtrahend, std::int64_t Addend) { + auto Left = symbolExpression(std::move(Minuend)); + if (!Left) + return std::unexpected(Left.error()); + auto Right = symbolExpression(std::move(Subtrahend)); + if (!Right) + return std::unexpected(Right.error()); + auto Difference = + mc::MCExpr::binary(mc::MCBinaryOperator::Subtract, std::move(*Left), std::move(*Right)); + if (!Difference) + return std::unexpected(Difference.error()); + return addConstant(std::move(*Difference), Addend); +} + +[[nodiscard]] Expected<mc::MCCFIInstruction> +lowerCFIInstruction(const MachineCFIInstruction &Instruction, MachineMCContext &Context) { + mc::MCCFIInstruction Result; + const auto Register = [&Result](const MachineCFIRegisterRule &Rule) { + Result.Operands.push_back(static_cast<std::int64_t>(Rule.Register.value())); + }; + const auto Offset = [&Result](const MachineCFIOffsetRule &Rule) { + Result.Operands.push_back(Rule.Offset); + }; + const auto RegisterOffset = [&Result](const MachineCFIRegisterOffsetRule &Rule) { + Result.Operands.push_back(static_cast<std::int64_t>(Rule.Register.value())); + Result.Operands.push_back(Rule.Offset); + }; + + switch (Instruction.operation()) { + case MachineCFIOperation::SameValue: + Result.Operation = mc::MCCFIOperation::SameValue; + Register(*Instruction.getIf<MachineCFIRegisterRule>()); + break; + case MachineCFIOperation::RememberState: + Result.Operation = mc::MCCFIOperation::RememberState; + break; + case MachineCFIOperation::RestoreState: + Result.Operation = mc::MCCFIOperation::RestoreState; + break; + case MachineCFIOperation::Offset: + Result.Operation = mc::MCCFIOperation::Offset; + RegisterOffset(*Instruction.getIf<MachineCFIRegisterOffsetRule>()); + break; + case MachineCFIOperation::DefCFARegister: + Result.Operation = mc::MCCFIOperation::DefCFARegister; + Register(*Instruction.getIf<MachineCFIRegisterRule>()); + break; + case MachineCFIOperation::DefCFAOffset: + Result.Operation = mc::MCCFIOperation::DefCFAOffset; + Offset(*Instruction.getIf<MachineCFIOffsetRule>()); + break; + case MachineCFIOperation::DefCFA: + Result.Operation = mc::MCCFIOperation::DefCFA; + RegisterOffset(*Instruction.getIf<MachineCFIRegisterOffsetRule>()); + break; + case MachineCFIOperation::RelativeOffset: + Result.Operation = mc::MCCFIOperation::RelativeOffset; + RegisterOffset(*Instruction.getIf<MachineCFIRegisterOffsetRule>()); + break; + case MachineCFIOperation::AdjustCFAOffset: + Result.Operation = mc::MCCFIOperation::AdjustCFAOffset; + Offset(*Instruction.getIf<MachineCFIOffsetRule>()); + break; + case MachineCFIOperation::Escape: + Result.Operation = mc::MCCFIOperation::Escape; + Result.Bytes = Instruction.getIf<MachineCFIEscapeRule>()->Bytes; + break; + case MachineCFIOperation::Restore: + Result.Operation = mc::MCCFIOperation::Restore; + Register(*Instruction.getIf<MachineCFIRegisterRule>()); + break; + case MachineCFIOperation::Undefined: + Result.Operation = mc::MCCFIOperation::Undefined; + Register(*Instruction.getIf<MachineCFIRegisterRule>()); + break; + case MachineCFIOperation::Register: { + Result.Operation = mc::MCCFIOperation::Register; + const auto &Rule = *Instruction.getIf<MachineCFIRegisterRegisterRule>(); + Result.Operands.push_back(static_cast<std::int64_t>(Rule.Register.value())); + Result.Operands.push_back(static_cast<std::int64_t>(Rule.SourceRegister.value())); + break; + } + case MachineCFIOperation::WindowSave: + Result.Operation = mc::MCCFIOperation::WindowSave; + break; + case MachineCFIOperation::NegateReturnAddressState: + Result.Operation = mc::MCCFIOperation::NegateReturnAddressState; + break; + case MachineCFIOperation::NegateReturnAddressStateWithPC: + Result.Operation = mc::MCCFIOperation::NegateReturnAddressStateWithPC; + break; + case MachineCFIOperation::GNUArgsSize: + Result.Operation = mc::MCCFIOperation::GNUArgsSize; + Offset(*Instruction.getIf<MachineCFIOffsetRule>()); + break; + case MachineCFIOperation::Label: { + Result.Operation = mc::MCCFIOperation::Label; + auto Symbol = Context.symbol(Instruction.getIf<MachineCFILabelRule>()->Symbol, + mc::MCSymbolKind::Temporary); + if (!Symbol) + return std::unexpected(Symbol.error()); + Result.Symbol = std::move(*Symbol); + break; + } + case MachineCFIOperation::ValueOffset: + Result.Operation = mc::MCCFIOperation::ValueOffset; + RegisterOffset(*Instruction.getIf<MachineCFIRegisterOffsetRule>()); + break; + case MachineCFIOperation::LLVMDefAddressSpaceCFA: + case MachineCFIOperation::LLVMRegisterPair: + case MachineCFIOperation::LLVMVectorRegisters: + case MachineCFIOperation::LLVMVectorOffset: + case MachineCFIOperation::LLVMVectorRegisterMask: + return makeUnexpected(ErrorCode::Unsupported, + "assembly emission does not support this LLVM-specific CFI operation"); + case MachineCFIOperation::Count: + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "machine CFI instruction has an invalid operation"); + } + + auto Verification = Result.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + return Result; +} + +[[nodiscard]] Expected<void> emitNamedLabel(const MachineOperand &Operand, + MachineMCContext &Context, mc::MCStreamer &Streamer) { + const auto *SymbolOperand = Operand.getIf<MCSymbolOperand>(); + if (SymbolOperand == nullptr || SymbolOperand->Offset != 0 || Operand.targetFlags() != 0) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "label pseudo requires an unmodified MC symbol operand"); + } + auto Symbol = Context.symbol(SymbolOperand->Name, mc::MCSymbolKind::Temporary); + if (!Symbol) + return std::unexpected(Symbol.error()); + return Streamer.emitLabel(std::move(*Symbol)); +} + +[[nodiscard]] bool validValueSize(std::uint64_t Size) noexcept { + return Size == 1 || Size == 2 || Size == 4 || Size == 8; +} + +[[nodiscard]] Expected<void> emitGlobalAttributes(const GlobalObjectDesc &Object, + const mc::MCSymbolRef &Symbol, + mc::MCStreamer &Streamer) { + switch (Object.Linkage) { + case GlobalLinkage::External: + case GlobalLinkage::Appending: + case GlobalLinkage::Common: { + auto Emitted = Streamer.emitSymbolAttribute(Symbol, mc::MCSymbolAttribute::Global); + if (!Emitted) + return std::unexpected(Emitted.error()); + break; + } + case GlobalLinkage::LinkOnceAny: + case GlobalLinkage::LinkOnceODR: + case GlobalLinkage::WeakAny: + case GlobalLinkage::WeakODR: + case GlobalLinkage::ExternalWeak: { + auto Emitted = Streamer.emitSymbolAttribute(Symbol, mc::MCSymbolAttribute::Weak); + if (!Emitted) + return std::unexpected(Emitted.error()); + break; + } + case GlobalLinkage::Internal: + case GlobalLinkage::Private: { + auto Emitted = Streamer.emitSymbolAttribute(Symbol, mc::MCSymbolAttribute::Local); + if (!Emitted) + return std::unexpected(Emitted.error()); + break; + } + case GlobalLinkage::AvailableExternally: + break; + case GlobalLinkage::Count: + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "global object has invalid linkage during assembly emission"); + } + + if (Object.Visibility == GlobalVisibility::Hidden) { + auto Emitted = Streamer.emitSymbolAttribute(Symbol, mc::MCSymbolAttribute::Hidden); + if (!Emitted) + return std::unexpected(Emitted.error()); + } else if (Object.Visibility == GlobalVisibility::Protected) { + auto Emitted = Streamer.emitSymbolAttribute(Symbol, mc::MCSymbolAttribute::Protected); + if (!Emitted) + return std::unexpected(Emitted.error()); + } + return Streamer.emitSymbolAttribute(Symbol, Object.TLSModel == GlobalTLSModel::None + ? mc::MCSymbolAttribute::Object + : mc::MCSymbolAttribute::TLSObject); +} + +[[nodiscard]] std::vector<std::uint8_t> bytesFromWords(std::span<const std::uint64_t> Words, + std::uint64_t Size) { + std::vector<std::uint8_t> Bytes(static_cast<std::size_t>(Size), 0); + for (std::uint64_t ByteIndex = 0; ByteIndex < Size; ++ByteIndex) { + const std::size_t WordIndex = static_cast<std::size_t>(ByteIndex / 8U); + if (WordIndex >= Words.size()) + break; + const unsigned Shift = static_cast<unsigned>((ByteIndex % 8U) * 8U); + Bytes[static_cast<std::size_t>(ByteIndex)] = + static_cast<std::uint8_t>((Words[WordIndex] >> Shift) & 0xffU); + } + return Bytes; +} + +[[nodiscard]] Expected<mc::MCExprRef> lowerConstantExpression(const GenericConstantData &Constant, + MachineMCContext &Context, + std::size_t Depth = 0) { + if (Depth > 1024) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "constant expression exceeds its nesting limit"); + } + switch (Constant.Kind) { + case GenericConstantKind::Integer: { + if (!Constant.IntegerValue || Constant.IntegerValue->bitWidth() > 64 || + Constant.IntegerValue->words().size() != 1) { + return makeUnexpected(ErrorCode::Unsupported, + "MC expression requires an integer no wider than 64 bits"); + } + const std::uint64_t Raw = Constant.IntegerValue->words().front(); + const std::int64_t Value = Constant.IntegerValue->bitWidth() == 64 + ? std::bit_cast<std::int64_t>(Raw) + : static_cast<std::int64_t>(Raw); + return mc::MCExpr::constant(Value); + } + case GenericConstantKind::NullPointer: + case GenericConstantKind::ZeroInitializer: + case GenericConstantKind::Undef: + case GenericConstantKind::Poison: + return mc::MCExpr::constant(0); + case GenericConstantKind::GlobalAddress: + case GenericConstantKind::DSOLocalEquivalent: + case GenericConstantKind::NoCFI: { + auto Symbol = Context.symbol(Constant.Symbol, mc::MCSymbolKind::Object); + if (!Symbol) + return std::unexpected(Symbol.error()); + return mc::MCExpr::symbol(std::move(*Symbol)); + } + case GenericConstantKind::BlockAddress: { + auto Symbol = Context.blockAddressSymbol(Constant.Symbol, Constant.Block); + if (!Symbol) + return std::unexpected(Symbol.error()); + return mc::MCExpr::symbol(std::move(*Symbol)); + } + case GenericConstantKind::Expression: { + if (Constant.Elements.empty()) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "generic constant expression has no operands"); + } + auto Left = lowerConstantExpression(Constant.Elements.front(), Context, Depth + 1); + if (!Left) + return std::unexpected(Left.error()); + switch (Constant.ExpressionOpcode) { + case GenericConstantExpressionOpcode::Trunc: + case GenericConstantExpressionOpcode::BitCast: + case GenericConstantExpressionOpcode::AddressSpaceCast: + case GenericConstantExpressionOpcode::IntToPtr: + case GenericConstantExpressionOpcode::PtrToAddr: + case GenericConstantExpressionOpcode::PtrToInt: + return *Left; + case GenericConstantExpressionOpcode::Add: + case GenericConstantExpressionOpcode::Sub: + case GenericConstantExpressionOpcode::Xor: { + if (Constant.Elements.size() != 2) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "binary constant expression has the wrong operand count"); + } + auto Right = lowerConstantExpression(Constant.Elements[1], Context, Depth + 1); + if (!Right) + return std::unexpected(Right.error()); + const mc::MCBinaryOperator Operator = + Constant.ExpressionOpcode == GenericConstantExpressionOpcode::Add + ? mc::MCBinaryOperator::Add + : Constant.ExpressionOpcode == GenericConstantExpressionOpcode::Sub + ? mc::MCBinaryOperator::Subtract + : mc::MCBinaryOperator::BitwiseXor; + return mc::MCExpr::binary(Operator, std::move(*Left), std::move(*Right)); + } + case GenericConstantExpressionOpcode::Invalid: + case GenericConstantExpressionOpcode::GetElementPtr: + case GenericConstantExpressionOpcode::ShuffleVector: + case GenericConstantExpressionOpcode::InsertElement: + case GenericConstantExpressionOpcode::ExtractElement: + case GenericConstantExpressionOpcode::Splat: + return makeUnexpected(ErrorCode::Unsupported, + "assembly cannot lower this generic constant expression"); + } + } + case GenericConstantKind::Invalid: + case GenericConstantKind::Floating: + case GenericConstantKind::Aggregate: + case GenericConstantKind::ByteString: + case GenericConstantKind::PointerAuth: + return makeUnexpected(ErrorCode::Unsupported, + "generic constant cannot be represented as an MC expression"); + } + return makeUnexpected(ErrorCode::InternalError, "generic constant kind is invalid"); +} + +[[nodiscard]] Expected<void> emitRepeatedByte(std::uint8_t Byte, std::uint64_t Size, + mc::MCStreamer &Streamer) { + if (Size == 0) + return {}; + if (Byte == 0) + return Streamer.emitZeroFill(Size); + constexpr std::uint64_t ChunkSize = 4096; + const std::size_t InitialSize = static_cast<std::size_t>(std::min(Size, ChunkSize)); + const std::vector<std::uint8_t> Chunk(InitialSize, Byte); + while (Size != 0) { + const std::size_t EmittedSize = static_cast<std::size_t>(std::min(Size, ChunkSize)); + auto Emitted = Streamer.emitBytes(std::span<const std::uint8_t>(Chunk).first(EmittedSize)); + if (!Emitted) + return std::unexpected(Emitted.error()); + Size -= static_cast<std::uint64_t>(EmittedSize); + } + return {}; +} + +} // namespace + +Expected<void> AsmPrinter::emitStartOfAsmFile() { + return {}; +} + +Expected<void> AsmPrinter::emitEndOfAsmFile() { + return {}; +} + +Expected<void> AsmPrinter::emitTargetConstant(const TargetConstant &) { + return makeUnexpected(ErrorCode::Unsupported, + "target does not support assembly emission for this target constant"); +} + +Expected<void> AsmPrinter::emitTargetJumpTable(const MachineFunction &, + const MachineJumpTableEntry &) { + return makeUnexpected(ErrorCode::Unsupported, "target does not support this jump-table encoding"); +} + +Expected<void> AsmPrinter::emitGenericConstant(const GenericConstantData &Constant, + std::uint64_t AllocationSize, std::size_t Depth) { + if (Depth > 1024) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "generic constant exceeds its emission nesting limit"); + } + if (Depth == 0) { + auto Verification = Constant.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + auto ExpectedSize = Layout.getTypeAllocSize(Constant.Type); + if (!ExpectedSize) + return std::unexpected(ExpectedSize.error()); + if (*ExpectedSize != AllocationSize) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "generic constant size does not match the target data layout"); + } + } + + switch (Constant.Kind) { + case GenericConstantKind::Integer: { + if (!Constant.IntegerValue) + return makeUnexpected(ErrorCode::InternalError, "integer constant has no value"); + return Streamer->emitBytes(bytesFromWords(Constant.IntegerValue->words(), AllocationSize)); + } + case GenericConstantKind::Floating: { + if (!Constant.FloatingValue) + return makeUnexpected(ErrorCode::InternalError, "floating constant has no value"); + return Streamer->emitBytes(bytesFromWords(Constant.FloatingValue->words(), AllocationSize)); + } + case GenericConstantKind::ByteString: { + if (Constant.ByteValue.size() != AllocationSize) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "byte-string constant size does not match its target layout"); + } + std::vector<std::uint8_t> Bytes; + Bytes.reserve(Constant.ByteValue.size()); + for (const std::byte Byte : Constant.ByteValue) + Bytes.push_back(std::to_integer<std::uint8_t>(Byte)); + return Streamer->emitBytes(Bytes); + } + case GenericConstantKind::NullPointer: + case GenericConstantKind::ZeroInitializer: + case GenericConstantKind::Undef: + case GenericConstantKind::Poison: + return Streamer->emitZeroFill(AllocationSize); + case GenericConstantKind::Aggregate: { + const GenericConstantType &Type = Constant.Type.resolved(); + std::uint64_t Offset = 0; + for (const GenericConstantData &Element : Constant.Elements) { + auto ElementSize = Layout.getTypeAllocSize(Element.Type); + auto ElementAlignment = Layout.getABITypeAlign(Element.Type); + if (!ElementSize) + return std::unexpected(ElementSize.error()); + if (!ElementAlignment) + return std::unexpected(ElementAlignment.error()); + const std::uint64_t Alignment = + Type.Kind == GenericConstantTypeKind::Struct && Type.Packed ? 1 : *ElementAlignment; + const std::uint64_t AlignmentMask = Alignment - 1U; + if (Offset > std::numeric_limits<std::uint64_t>::max() - AlignmentMask) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "aggregate constant exceeds its target layout"); + } + const std::uint64_t ElementOffset = (Offset + AlignmentMask) & ~AlignmentMask; + if (ElementOffset > AllocationSize || *ElementSize > AllocationSize - ElementOffset) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "aggregate constant exceeds its target layout"); + } + auto Padding = Streamer->emitZeroFill(ElementOffset - Offset); + if (!Padding) + return std::unexpected(Padding.error()); + auto Emitted = emitGenericConstant(Element, *ElementSize, Depth + 1); + if (!Emitted) + return std::unexpected(Emitted.error()); + Offset = ElementOffset + *ElementSize; + } + if (Offset > AllocationSize) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "aggregate constant exceeds its target layout"); + } + return Streamer->emitZeroFill(AllocationSize - Offset); + } + case GenericConstantKind::GlobalAddress: + case GenericConstantKind::BlockAddress: + case GenericConstantKind::DSOLocalEquivalent: + case GenericConstantKind::NoCFI: + case GenericConstantKind::Expression: { + if (!validValueSize(AllocationSize)) { + return makeUnexpected(ErrorCode::Unsupported, + "symbolic constant has an unsupported storage size"); + } + auto Expression = lowerConstantExpression(Constant, *Context); + if (!Expression) + return std::unexpected(Expression.error()); + return Streamer->emitValue(std::move(*Expression), static_cast<std::uint8_t>(AllocationSize)); + } + case GenericConstantKind::PointerAuth: + return makeUnexpected(ErrorCode::Unsupported, + "assembly does not support pointer-auth constants"); + case GenericConstantKind::Invalid: + return makeUnexpected(ErrorCode::InternalError, "generic constant kind is invalid"); + } + return makeUnexpected(ErrorCode::InternalError, "generic constant kind is invalid"); +} + +Expected<void> AsmPrinter::emitGlobalInitializer(const GlobalInitializer &Initializer, + std::size_t Depth) { + if (Depth > 1024) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "global initializer exceeds its emission nesting limit"); + } + if (Depth == 0) { + auto Verification = Initializer.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + } + + switch (Initializer.kind()) { + case GlobalInitializer::Kind::Integer: { + const auto *Value = Initializer.getIf<APInt>(); + if (Value == nullptr) + return makeUnexpected(ErrorCode::InternalError, "integer initializer has no value"); + const std::uint64_t Size = (static_cast<std::uint64_t>(Value->bitWidth()) + 7U) / 8U; + return Streamer->emitBytes(bytesFromWords(Value->words(), Size)); + } + case GlobalInitializer::Kind::Floating: { + const auto *Value = Initializer.getIf<APFloat>(); + if (Value == nullptr) + return makeUnexpected(ErrorCode::InternalError, "floating initializer has no value"); + const std::uint64_t Size = (static_cast<std::uint64_t>(Value->bitWidth()) + 7U) / 8U; + return Streamer->emitBytes(bytesFromWords(Value->words(), Size)); + } + case GlobalInitializer::Kind::Bytes: { + const auto *Value = Initializer.getIf<std::vector<std::byte>>(); + if (Value == nullptr) + return makeUnexpected(ErrorCode::InternalError, "byte initializer has no value"); + std::vector<std::uint8_t> Bytes; + Bytes.reserve(Value->size()); + for (const std::byte Byte : *Value) + Bytes.push_back(std::to_integer<std::uint8_t>(Byte)); + return Streamer->emitBytes(Bytes); + } + case GlobalInitializer::Kind::ZeroFill: { + const auto *Value = Initializer.getIf<GlobalZeroFill>(); + return Value == nullptr + ? makeUnexpected(ErrorCode::InternalError, "zero-fill initializer has no value") + : Streamer->emitZeroFill(Value->Size); + } + case GlobalInitializer::Kind::Repeated: { + const auto *Value = Initializer.getIf<GlobalRepeatedInitializer>(); + if (Value == nullptr || !Value->Value) + return makeUnexpected(ErrorCode::InternalError, "repeated initializer has no value"); + for (std::uint64_t Index = 0; Index < Value->Count; ++Index) { + auto Emitted = emitGlobalInitializer(*Value->Value, Depth + 1); + if (!Emitted) + return std::unexpected(Emitted.error()); + } + return {}; + } + case GlobalInitializer::Kind::Aggregate: { + const auto *Value = Initializer.getIf<GlobalAggregateInitializer>(); + if (Value == nullptr) + return makeUnexpected(ErrorCode::InternalError, "aggregate initializer has no value"); + for (const GlobalInitializerRef &Element : Value->Elements) { + if (!Element) + return makeUnexpected(ErrorCode::InternalError, "aggregate initializer is null"); + auto Emitted = emitGlobalInitializer(*Element, Depth + 1); + if (!Emitted) + return std::unexpected(Emitted.error()); + } + return {}; + } + case GlobalInitializer::Kind::SymbolAddress: { + const auto *Value = Initializer.getIf<GlobalSymbolAddress>(); + if (Value == nullptr) + return makeUnexpected(ErrorCode::InternalError, "symbol initializer has no value"); + auto Symbol = Context->symbol(Value->Symbol, mc::MCSymbolKind::Object); + if (!Symbol) + return std::unexpected(Symbol.error()); + auto Expression = symbolExpression(std::move(*Symbol)); + if (!Expression) + return std::unexpected(Expression.error()); + auto WithAddend = addConstant(std::move(*Expression), Value->Addend); + if (!WithAddend) + return std::unexpected(WithAddend.error()); + auto Lowered = Lowering->lowerSymbolExpression(std::move(*WithAddend), Value->TargetFlags); + if (!Lowered) + return std::unexpected(Lowered.error()); + return Streamer->emitValue(std::move(*Lowered), Value->Size); + } + case GlobalInitializer::Kind::SymbolDifference: { + const auto *Value = Initializer.getIf<GlobalSymbolDifference>(); + if (Value == nullptr) + return makeUnexpected(ErrorCode::InternalError, "symbol difference has no value"); + auto Minuend = Context->symbol(Value->Minuend); + if (!Minuend) + return std::unexpected(Minuend.error()); + auto Subtrahend = Context->symbol(Value->Subtrahend); + if (!Subtrahend) + return std::unexpected(Subtrahend.error()); + auto Difference = symbolDifference(std::move(*Minuend), std::move(*Subtrahend), Value->Addend); + if (!Difference) + return std::unexpected(Difference.error()); + return Streamer->emitValue(std::move(*Difference), Value->Size); + } + case GlobalInitializer::Kind::Target: { + const auto *Value = Initializer.getIf<TargetConstant>(); + if (Value == nullptr) + return makeUnexpected(ErrorCode::InternalError, "target initializer has no value"); + return emitTargetConstant(*Value); + } + case GlobalInitializer::Kind::AlignmentPadding: { + const auto *Value = Initializer.getIf<GlobalAlignmentPadding>(); + return Value == nullptr + ? makeUnexpected(ErrorCode::InternalError, "padding initializer has no value") + : emitRepeatedByte(Value->Fill, Value->Size, *Streamer); + } + case GlobalInitializer::Kind::UndefinedStorage: { + const auto *Value = Initializer.getIf<GlobalUndefinedStorage>(); + return Value == nullptr + ? makeUnexpected(ErrorCode::InternalError, "undefined initializer has no value") + : Streamer->emitZeroFill(Value->Size); + } + case GlobalInitializer::Kind::Invalid: + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "global initializer is invalid"); + } + return makeUnexpected(ErrorCode::InternalError, "global initializer kind is invalid"); +} + +Expected<void> AsmPrinter::verifyFunction(const MachineFunction &Function) const { + const MachineFunctionProperties Required = { + MachineFunctionProperty::NoPHIs, + MachineFunctionProperty::Legalized, + MachineFunctionProperty::RegBankSelected, + MachineFunctionProperty::Selected, + MachineFunctionProperty::TiedOpsRewritten, + MachineFunctionProperty::NoVRegs, + MachineFunctionProperty::FrameFinalized, + MachineFunctionProperty::NoFrameIndices, + MachineFunctionProperty::NoExecutablePseudos, + }; + if (!Function.properties().containsAll(Required)) { + const MachineFunctionProperties Missing = Required.missingFrom(Function.properties()); + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "assembly emission requires finalized MIR properties: " + + Missing.toString()); + } + if (!Options.VerifyInput) + return {}; + return MachineVerifier::verify(Function, *Target, + MachineVerifierOptions{.Mode = MachineVerifierMode::Generic, + .Stage = PipelineStage::Emission, + .VerifyProperties = true}); +} + +Expected<void> AsmPrinter::emitCompilationUnit(const CompilationUnit &Unit) { + if (EmissionStarted) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "an AsmPrinter can only emit one compilation unit or function"); + } + EmissionStarted = true; + + auto Initialized = Context->initialize(Unit); + if (!Initialized) + return std::unexpected(Initialized.error()); + auto Header = emitStartOfAsmFile(); + if (!Header) + return std::unexpected(Header.error()); + + auto Globals = emitGlobalObjects(Unit); + if (!Globals) + return std::unexpected(Globals.error()); + + for (std::size_t Index = 0; Index < Unit.functionCount(); ++Index) { + const MachineFunction *Function = Unit.functionAt(Index); + if (Function == nullptr) { + return makeUnexpected(ErrorCode::InternalError, + "compilation unit contains a null function during assembly emission"); + } + auto Emitted = emitFunctionImpl(*Function); + if (!Emitted) + return std::unexpected(Emitted.error()); + } + + auto Trailer = emitEndOfAsmFile(); + if (!Trailer) + return std::unexpected(Trailer.error()); + auto Finished = Streamer->finish(); + if (!Finished) + return std::unexpected(Finished.error()); + EmissionFinished = true; + return {}; +} + +Expected<void> AsmPrinter::emitGlobalObjects(const CompilationUnit &Unit) { + for (std::size_t Index = 0; Index < Unit.globalObjectCount(); ++Index) { + const GlobalObjectDesc *Object = Unit.globalObjectAt(Index); + if (Object == nullptr) + return makeUnexpected(ErrorCode::InternalError, "compilation unit contains a null global"); + if (Object->Linkage == GlobalLinkage::AvailableExternally) + continue; + + const mc::MCSymbolRef Symbol = Context->globalSymbol(*Object); + if (!Symbol) + return makeUnexpected(ErrorCode::InternalError, "global object has no MC symbol"); + auto Attributes = emitGlobalAttributes(*Object, Symbol, *Streamer); + if (!Attributes) + return std::unexpected(Attributes.error()); + if (Object->isDeclaration()) + continue; + + if (Object->Linkage == GlobalLinkage::Common) { + auto Common = Streamer->emitCommonSymbol(Symbol, Object->Size, Object->Align.bytes()); + if (!Common) + return std::unexpected(Common.error()); + continue; + } + + auto Description = ObjectFile->globalSection(*Object, *Context); + if (!Description) + return std::unexpected(Description.error()); + auto Section = Context->context().getOrCreateSection(std::move(*Description)); + if (!Section) + return std::unexpected(Section.error()); + auto Switched = Streamer->switchSection(std::move(*Section)); + if (!Switched) + return std::unexpected(Switched.error()); + auto Aligned = Streamer->emitAlignment(Object->Align.bytes()); + if (!Aligned) + return std::unexpected(Aligned.error()); + auto Label = Streamer->emitLabel(Symbol); + if (!Label) + return std::unexpected(Label.error()); + if (!Object->Initializer) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "defined global object has no initializer"); + } + auto Initializer = emitGlobalInitializer(*Object->Initializer); + if (!Initializer) + return std::unexpected(Initializer.error()); + auto Size = Streamer->emitSymbolSize( + Symbol, mc::MCExpr::constant(static_cast<std::int64_t>(Object->Size))); + if (!Size) + return std::unexpected(Size.error()); + } + return {}; +} + +Expected<void> AsmPrinter::emitFunction(const MachineFunction &Function) { + if (EmissionStarted) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "an AsmPrinter can only emit one compilation unit or function"); + } + EmissionStarted = true; + auto Header = emitStartOfAsmFile(); + if (!Header) + return std::unexpected(Header.error()); + auto Emitted = emitFunctionImpl(Function); + if (!Emitted) + return std::unexpected(Emitted.error()); + auto Trailer = emitEndOfAsmFile(); + if (!Trailer) + return std::unexpected(Trailer.error()); + auto Finished = Streamer->finish(); + if (!Finished) + return std::unexpected(Finished.error()); + EmissionFinished = true; + return {}; +} + +Expected<void> AsmPrinter::emitFunctionImpl(const MachineFunction &Function) { + if (EmissionFinished) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "cannot emit a function after the assembly stream is finalized"); + } + auto Verification = verifyFunction(Function); + if (!Verification) + return std::unexpected(Verification.error()); + auto Initialized = Context->initializeFunction(Function); + if (!Initialized) + return std::unexpected(Initialized.error()); + + auto SectionDescription = ObjectFile->textSection(Function); + if (!SectionDescription) + return std::unexpected(SectionDescription.error()); + auto Section = Context->context().getOrCreateSection(std::move(*SectionDescription)); + if (!Section) + return std::unexpected(Section.error()); + auto Switched = Streamer->switchSection(std::move(*Section)); + if (!Switched) + return std::unexpected(Switched.error()); + + const mc::MCSymbolRef FunctionSymbol = Context->functionSymbol(Function); + if (!FunctionSymbol) { + return makeUnexpected(ErrorCode::InternalError, + "function has no MC symbol after context initialization"); + } + if (Options.EmitFunctionDirectives) { + auto Global = Streamer->emitSymbolAttribute(FunctionSymbol, mc::MCSymbolAttribute::Global); + if (!Global) + return std::unexpected(Global.error()); + auto Type = Streamer->emitSymbolAttribute(FunctionSymbol, mc::MCSymbolAttribute::Function); + if (!Type) + return std::unexpected(Type.error()); + } + if (const auto Alignment = Function.attributes().alignment(); + Alignment && Alignment->bytes() > 1) { + auto Aligned = Streamer->emitAlignment(Alignment->bytes()); + if (!Aligned) + return std::unexpected(Aligned.error()); + } + auto FunctionLabel = Streamer->emitLabel(FunctionSymbol); + if (!FunctionLabel) + return std::unexpected(FunctionLabel.error()); + if (Options.EmitCFI) { + mc::MCCFIInstruction StartProc; + StartProc.Operation = mc::MCCFIOperation::StartProc; + auto CFIStart = Streamer->emitCFI(StartProc); + if (!CFIStart) + return std::unexpected(CFIStart.error()); + } + + auto Body = emitFunctionBody(Function); + if (!Body) + return std::unexpected(Body.error()); + if (Options.EmitCFI) { + mc::MCCFIInstruction EndProc; + EndProc.Operation = mc::MCCFIOperation::EndProc; + auto CFIEnd = Streamer->emitCFI(EndProc); + if (!CFIEnd) + return std::unexpected(CFIEnd.error()); + } + + auto EndSymbol = Context->symbol(".Lfunc_end" + std::to_string(Function.stableID().value()), + mc::MCSymbolKind::Temporary); + if (!EndSymbol) + return std::unexpected(EndSymbol.error()); + auto EndLabel = Streamer->emitLabel(*EndSymbol); + if (!EndLabel) + return std::unexpected(EndLabel.error()); + if (Options.EmitFunctionDirectives) { + auto Size = symbolDifference(*EndSymbol, FunctionSymbol, 0); + if (!Size) + return std::unexpected(Size.error()); + auto EmittedSize = Streamer->emitSymbolSize(FunctionSymbol, std::move(*Size)); + if (!EmittedSize) + return std::unexpected(EmittedSize.error()); + } + + auto Constants = emitConstantPool(Function); + if (!Constants) + return std::unexpected(Constants.error()); + return emitJumpTables(Function); +} + +Expected<void> AsmPrinter::emitFunctionBody(const MachineFunction &Function) { + for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; + Block = Block->next()) { + if (Block->attributes().alignment().bytes() > 1) { + auto Aligned = Streamer->emitAlignment(Block->attributes().alignment().bytes()); + if (!Aligned) + return std::unexpected(Aligned.error()); + } + if (Options.EmitBlockLabels) { + const mc::MCSymbolRef Symbol = Context->blockSymbol(*Block); + if (!Symbol) + return makeUnexpected(ErrorCode::InternalError, "machine block has no MC symbol"); + auto Label = Streamer->emitLabel(Symbol); + if (!Label) + return std::unexpected(Label.error()); + } + + for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr; + Instruction = Instruction->next()) { + const InstructionDescriptor *Descriptor = + Target->instructionDescriptor(Instruction->opcode()); + if (Descriptor == nullptr) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "assembly emission encountered an unknown opcode"); + } + if (Options.EmitInstructionSymbols) { + if (const auto SymbolName = Instruction->preInstrSymbol()) { + auto Symbol = Context->symbol(std::string(*SymbolName), mc::MCSymbolKind::Temporary); + if (!Symbol) + return std::unexpected(Symbol.error()); + auto Label = Streamer->emitLabel(std::move(*Symbol)); + if (!Label) + return std::unexpected(Label.error()); + } + } + + Expected<void> Emitted; + if (Instruction->opcode() == opcode(GenericOpcode::CFI_INSTRUCTION)) { + const auto *Index = Instruction->operandAt(0) == nullptr + ? nullptr + : Instruction->operandAt(0)->getIf<CFIIndexOperand>(); + const MachineCFIInstruction *CFI = + Index == nullptr ? nullptr : Function.cfiInfo().instruction(Index->Index); + if (CFI == nullptr) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "CFI pseudo references an invalid CFI instruction"); + } + if (Options.EmitCFI) { + auto Lowered = lowerCFIInstruction(*CFI, *Context); + if (!Lowered) + return std::unexpected(Lowered.error()); + Emitted = Streamer->emitCFI(*Lowered); + } + } else if (Instruction->opcode() == opcode(GenericOpcode::EH_LABEL) || + Instruction->opcode() == opcode(GenericOpcode::GC_LABEL) || + Instruction->opcode() == opcode(GenericOpcode::ANNOTATION_LABEL)) { + if (Instruction->operandAt(0) == nullptr) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "label pseudo has no symbol operand"); + } + Emitted = emitNamedLabel(*Instruction->operandAt(0), *Context, *Streamer); + } else if (Descriptor->Flags.has(InstructionFlag::TerminalEmission)) { + Emitted = Lowering->emitTerminalInstruction(*Instruction, *Context, *Streamer); + } else if (Descriptor->Flags.has(InstructionFlag::Meta)) { + Emitted = {}; + } else if (Descriptor->Flags.has(InstructionFlag::ProducesCode)) { + auto Lowered = Lowering->lowerInstruction(*Instruction, *Context); + if (!Lowered) + return std::unexpected(Lowered.error()); + Emitted = Streamer->emitInstruction(*Lowered); + } else { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "non-meta instruction has no assembly emission behavior"); + } + if (!Emitted) + return std::unexpected(Emitted.error()); + + if (Options.EmitInstructionSymbols) { + if (const auto SymbolName = Instruction->postInstrSymbol()) { + auto Symbol = Context->symbol(std::string(*SymbolName), mc::MCSymbolKind::Temporary); + if (!Symbol) + return std::unexpected(Symbol.error()); + auto Label = Streamer->emitLabel(std::move(*Symbol)); + if (!Label) + return std::unexpected(Label.error()); + } + } + } + } + return {}; +} + +Expected<void> AsmPrinter::emitConstantPool(const MachineFunction &Function) { + for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) { + const MachineConstantPoolEntryDesc *Description = Entry.description(); + if (Description == nullptr) + continue; + auto SectionDescription = ObjectFile->constantPoolSection(Function, Entry); + if (!SectionDescription) + return std::unexpected(SectionDescription.error()); + auto Section = Context->context().getOrCreateSection(std::move(*SectionDescription)); + if (!Section) + return std::unexpected(Section.error()); + auto Switched = Streamer->switchSection(std::move(*Section)); + if (!Switched) + return std::unexpected(Switched.error()); + auto Aligned = Streamer->emitAlignment(Description->Align.bytes()); + if (!Aligned) + return std::unexpected(Aligned.error()); + + const mc::MCSymbolRef Symbol = Context->constantPoolSymbol(Function, Entry.index()); + if (!Symbol) + return makeUnexpected(ErrorCode::InternalError, "constant-pool entry has no MC symbol"); + auto Label = Streamer->emitLabel(Symbol); + if (!Label) + return std::unexpected(Label.error()); + + Expected<void> Emitted; + if (const auto *Generic = std::get_if<GenericConstantData>(&Description->Value)) { + Emitted = emitGenericConstant(*Generic, Description->Size); + } else if (const auto *TargetValue = std::get_if<TargetConstant>(&Description->Value)) { + Emitted = emitTargetConstant(*TargetValue); + } else if (const auto *Difference = + std::get_if<SymbolDifferenceConstant>(&Description->Value)) { + if (!validValueSize(Description->Size)) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "symbol-difference constant has an unsupported storage size"); + } + auto Minuend = Context->symbol(Difference->Minuend); + if (!Minuend) + return std::unexpected(Minuend.error()); + auto Subtrahend = Context->symbol(Difference->Subtrahend); + if (!Subtrahend) + return std::unexpected(Subtrahend.error()); + auto Expression = symbolDifference(*Minuend, *Subtrahend, Difference->Addend); + if (!Expression) + return std::unexpected(Expression.error()); + Emitted = + Streamer->emitValue(std::move(*Expression), static_cast<std::uint8_t>(Description->Size)); + } else if (const auto *Address = std::get_if<BlockAddressConstant>(&Description->Value)) { + if (!validValueSize(Description->Size)) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "block-address constant has an unsupported storage size"); + } + auto AddressSymbol = Context->blockAddressSymbol(Address->FunctionName, Address->BlockName); + if (!AddressSymbol) + return std::unexpected(AddressSymbol.error()); + auto Expression = symbolExpression(std::move(*AddressSymbol)); + if (!Expression) + return std::unexpected(Expression.error()); + Expression = addConstant(std::move(*Expression), Address->Addend); + if (!Expression) + return std::unexpected(Expression.error()); + Emitted = + Streamer->emitValue(std::move(*Expression), static_cast<std::uint8_t>(Description->Size)); + } else if (const auto *TargetExpression = + std::get_if<TargetExpressionConstant>(&Description->Value)) { + Emitted = emitTargetConstant(TargetExpression->Expression); + } else { + return makeUnexpected(ErrorCode::InternalError, "constant-pool value has an invalid kind"); + } + if (!Emitted) + return std::unexpected(Emitted.error()); + } + return {}; +} + +Expected<void> AsmPrinter::emitJumpTables(const MachineFunction &Function) { + for (const MachineJumpTableEntry &Entry : Function.jumpTableInfo().tables()) { + const MachineJumpTableDesc *Description = Entry.description(); + if (Description == nullptr) + continue; + auto SectionDescription = ObjectFile->jumpTableSection(Function, Entry); + if (!SectionDescription) + return std::unexpected(SectionDescription.error()); + auto Section = Context->context().getOrCreateSection(std::move(*SectionDescription)); + if (!Section) + return std::unexpected(Section.error()); + auto Switched = Streamer->switchSection(std::move(*Section)); + if (!Switched) + return std::unexpected(Switched.error()); + auto Aligned = Streamer->emitAlignment(Description->Align.bytes()); + if (!Aligned) + return std::unexpected(Aligned.error()); + + const mc::MCSymbolRef TableSymbol = Context->jumpTableSymbol(Function, Entry.index()); + if (!TableSymbol) + return makeUnexpected(ErrorCode::InternalError, "jump-table entry has no MC symbol"); + auto Label = Streamer->emitLabel(TableSymbol); + if (!Label) + return std::unexpected(Label.error()); + + if (Description->Encoding != JumpTableEntryEncoding::BlockAddress && + Description->Encoding != JumpTableEntryEncoding::LabelDifference32 && + Description->Encoding != JumpTableEntryEncoding::LabelDifference64) { + auto TargetTable = emitTargetJumpTable(Function, Entry); + if (!TargetTable) + return std::unexpected(TargetTable.error()); + continue; + } + + for (const MachineBasicBlock *TargetBlock : Description->Targets) { + if (TargetBlock == nullptr) + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "jump table has a null target"); + const mc::MCSymbolRef TargetSymbol = Context->blockSymbol(*TargetBlock); + if (!TargetSymbol) + return makeUnexpected(ErrorCode::InternalError, "jump-table target has no MC symbol"); + + Expected<mc::MCExprRef> Expression = symbolExpression(TargetSymbol); + if (Description->Encoding == JumpTableEntryEncoding::LabelDifference32 || + Description->Encoding == JumpTableEntryEncoding::LabelDifference64) + Expression = symbolDifference(TargetSymbol, TableSymbol, 0); + if (!Expression) + return std::unexpected(Expression.error()); + auto Value = Streamer->emitValue(std::move(*Expression), Description->EntrySize); + if (!Value) + return std::unexpected(Value.error()); + } + } + return {}; +} + +} // namespace rcc diff --git a/backend/lib/AsmPrinter/CMakeLists.txt b/backend/lib/AsmPrinter/CMakeLists.txt new file mode 100644 index 0000000..f046e3a --- /dev/null +++ b/backend/lib/AsmPrinter/CMakeLists.txt @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +set( + RCCBackendAsmPrinterSources + AsmPrinter.cpp + MachineMCContext.cpp + TargetMCInstLowering.cpp +) +set( + RCCBackendAsmPrinterHeaders + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/AsmPrinter.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineMCContext.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/TargetLoweringObjectFile.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/TargetMCInstLowering.h" +) + +add_library(RCCBackendAsmPrinter ${RCCBackendAsmPrinterSources}) +add_library(RCCBackend::AsmPrinter ALIAS RCCBackendAsmPrinter) + +rcc_backend_configure_target(RCCBackendAsmPrinter) + +target_include_directories( + RCCBackendAsmPrinter + PUBLIC + "$<BUILD_INTERFACE:${RCC_BACKEND_SOURCE_DIR}/include>" + "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" +) +target_link_libraries( + RCCBackendAsmPrinter + PUBLIC + RCCBackend::CodeGen + RCCBackend::MC +) + +set_target_properties( + RCCBackendAsmPrinter + PROPERTIES + EXPORT_NAME AsmPrinter + OUTPUT_NAME RCCBackendAsmPrinter +) + +install( + TARGETS RCCBackendAsmPrinter + EXPORT RCCBackendTargets + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" +) + +set(RCCBackendAsmPrinterSourcePaths) +foreach(Source IN LISTS RCCBackendAsmPrinterSources) + list(APPEND RCCBackendAsmPrinterSourcePaths "${CMAKE_CURRENT_SOURCE_DIR}/${Source}") +endforeach() + +rcc_backend_register_format_files( + ${RCCBackendAsmPrinterSourcePaths} + ${RCCBackendAsmPrinterHeaders} +) diff --git a/backend/lib/AsmPrinter/MachineMCContext.cpp b/backend/lib/AsmPrinter/MachineMCContext.cpp new file mode 100644 index 0000000..163e228 --- /dev/null +++ b/backend/lib/AsmPrinter/MachineMCContext.cpp @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/MachineMCContext.h" + +#include "rcc/CodeGen/CompilationUnit.h" +#include "rcc/CodeGen/GlobalObject.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineConstantPool.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineJumpTableInfo.h" + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <optional> +#include <string> +#include <string_view> +#include <utility> + +namespace rcc { + +std::size_t MachineMCContext::FunctionIndexHash::operator()(FunctionIndexKey Key) const noexcept { + const std::size_t FunctionHash = std::hash<const MachineFunction *>{}(Key.Function); + const std::size_t IndexHash = std::hash<std::uint32_t>{}(Key.Index); + return FunctionHash ^ (IndexHash + 0x9e3779b9U + (FunctionHash << 6U) + (FunctionHash >> 2U)); +} + +Expected<void> MachineMCContext::initialize(const CompilationUnit &Unit) { + auto Verification = Unit.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + for (std::size_t Index = 0; Index < Unit.globalObjectCount(); ++Index) { + const GlobalObjectDesc *Object = Unit.globalObjectAt(Index); + if (Object == nullptr) + return makeUnexpected(ErrorCode::InternalError, "compilation unit contains a null global"); + auto Symbol = Context->getOrCreateSymbol(Object->Symbol, mc::MCSymbolKind::Object); + if (!Symbol) + return std::unexpected(Symbol.error()); + GlobalSymbols.emplace(Object, std::move(*Symbol)); + } + for (std::size_t Index = 0; Index < Unit.functionCount(); ++Index) { + const MachineFunction *Function = Unit.functionAt(Index); + if (Function == nullptr) + return makeUnexpected(ErrorCode::InternalError, "compilation unit contains a null function"); + auto Initialized = initializeFunction(*Function); + if (!Initialized) + return std::unexpected(Initialized.error()); + } + return {}; +} + +Expected<void> MachineMCContext::initializeFunction(const MachineFunction &Function) { + if (FunctionSymbols.contains(&Function)) + return {}; + + auto FunctionSymbol = + Context->getOrCreateSymbol(std::string(Function.name()), mc::MCSymbolKind::Function); + if (!FunctionSymbol) + return std::unexpected(FunctionSymbol.error()); + FunctionSymbols.emplace(&Function, *FunctionSymbol); + + for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; + Block = Block->next()) { + const std::optional<std::size_t> DenseNumber = Block->denseNumber(); + if (!Block->isLinked() || !DenseNumber) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "MC symbol initialization requires linked and numbered blocks"); + } + const std::string Name = + ".LBB" + std::to_string(Function.stableID().value()) + "_" + std::to_string(*DenseNumber); + auto Symbol = Context->getOrCreateSymbol(Name, mc::MCSymbolKind::BasicBlock); + if (!Symbol) + return std::unexpected(Symbol.error()); + BlockSymbols.emplace(Block, *Symbol); + } + + for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) { + if (Entry.isTombstone()) + continue; + const std::string Name = ".LCPI" + std::to_string(Function.stableID().value()) + "_" + + std::to_string(Entry.index().value()); + auto Symbol = Context->getOrCreateSymbol(Name, mc::MCSymbolKind::ConstantPool); + if (!Symbol) + return std::unexpected(Symbol.error()); + ConstantPoolSymbols.emplace( + FunctionIndexKey{.Function = &Function, .Index = Entry.index().value()}, *Symbol); + } + + for (const MachineJumpTableEntry &Entry : Function.jumpTableInfo().tables()) { + if (Entry.isTombstone() || Entry.isPending()) + continue; + const std::string Name = ".LJTI" + std::to_string(Function.stableID().value()) + "_" + + std::to_string(Entry.index().value()); + auto Symbol = Context->getOrCreateSymbol(Name, mc::MCSymbolKind::JumpTable); + if (!Symbol) + return std::unexpected(Symbol.error()); + JumpTableSymbols.emplace( + FunctionIndexKey{.Function = &Function, .Index = Entry.index().value()}, *Symbol); + } + return {}; +} + +mc::MCSymbolRef MachineMCContext::functionSymbol(const MachineFunction &Function) const noexcept { + const auto Position = FunctionSymbols.find(&Function); + return Position == FunctionSymbols.end() ? mc::MCSymbolRef{} : Position->second; +} + +mc::MCSymbolRef MachineMCContext::globalSymbol(const GlobalObjectDesc &Object) const noexcept { + const auto Position = GlobalSymbols.find(&Object); + return Position == GlobalSymbols.end() ? mc::MCSymbolRef{} : Position->second; +} + +mc::MCSymbolRef MachineMCContext::blockSymbol(const MachineBasicBlock &Block) const noexcept { + const auto Position = BlockSymbols.find(&Block); + return Position == BlockSymbols.end() ? mc::MCSymbolRef{} : Position->second; +} + +mc::MCSymbolRef MachineMCContext::constantPoolSymbol(const MachineFunction &Function, + ConstantPoolIndex Index) const noexcept { + const auto Position = + ConstantPoolSymbols.find(FunctionIndexKey{.Function = &Function, .Index = Index.value()}); + return Position == ConstantPoolSymbols.end() ? mc::MCSymbolRef{} : Position->second; +} + +mc::MCSymbolRef MachineMCContext::jumpTableSymbol(const MachineFunction &Function, + JumpTableIndex Index) const noexcept { + const auto Position = + JumpTableSymbols.find(FunctionIndexKey{.Function = &Function, .Index = Index.value()}); + return Position == JumpTableSymbols.end() ? mc::MCSymbolRef{} : Position->second; +} + +Expected<mc::MCSymbolRef> MachineMCContext::symbol(std::string Name, mc::MCSymbolKind Kind) { + return Context->getOrCreateSymbol(std::move(Name), Kind); +} + +Expected<mc::MCSymbolRef> MachineMCContext::blockAddressSymbol(std::string_view FunctionName, + std::string_view BlockName) const { + for (const auto &[Block, Symbol] : BlockSymbols) { + if (Block == nullptr || Block->function().name() != FunctionName) + continue; + const auto IRName = Block->attributes().irBlockName(); + const auto MachineName = Block->attributes().name(); + const bool NumericMatch = + Block->denseNumber() && std::to_string(*Block->denseNumber()) == BlockName; + if ((IRName && *IRName == BlockName) || (MachineName && *MachineName == BlockName) || + NumericMatch) + return Symbol; + } + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "block-address operand does not resolve to an emitted block symbol"); +} + +} // namespace rcc diff --git a/backend/lib/AsmPrinter/TargetMCInstLowering.cpp b/backend/lib/AsmPrinter/TargetMCInstLowering.cpp new file mode 100644 index 0000000..898ca2e --- /dev/null +++ b/backend/lib/AsmPrinter/TargetMCInstLowering.cpp @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/TargetMCInstLowering.h" + +namespace rcc { + +Expected<void> TargetMCInstLowering::emitTerminalInstruction(const MachineInstr &, + MachineMCContext &, + mc::MCStreamer &) const { + return makeUnexpected(ErrorCode::Unsupported, + "target does not support this terminal emission instruction"); +} + +} // namespace rcc |
