diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-28 14:25:42 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-28 14:25:42 +0800 |
| commit | 6889b82a5c9ecc6bd178760c66653d7af0121162 (patch) | |
| tree | d0465db788912487c36bd676656f416eaa8b1642 | |
| parent | 6e8feb15a0d256ca19e88185f78c81f689ce7d34 (diff) | |
| download | rcc-project-6889b82a5c9ecc6bd178760c66653d7af0121162.tar.gz rcc-project-6889b82a5c9ecc6bd178760c66653d7af0121162.tar.xz | |
[CodeGen] Separate assembly emission ownership
| -rw-r--r-- | backend/include/rcc/CodeGen/AsmPrinter.h | 45 | ||||
| -rw-r--r-- | backend/include/rcc/CodeGen/TargetLoweringObjectFile.h | 34 | ||||
| -rw-r--r-- | backend/include/rcc/CodeGen/TargetMCInstLowering.h | 41 | ||||
| -rw-r--r-- | backend/include/rcc/Target/RISCV/RISCVMCTargetLowering.h | 31 | ||||
| -rw-r--r-- | backend/lib/AsmPrinter/AsmPrinter.cpp | 437 | ||||
| -rw-r--r-- | backend/lib/AsmPrinter/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | backend/lib/AsmPrinter/TargetMCInstLowering.cpp | 35 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVAsmPrinter.cpp | 190 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVAsmPrinter.h | 28 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVAssemblyPrinter.cpp | 29 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp | 788 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVObjectFile.cpp | 28 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVTargetObjectFile.cpp | 244 | ||||
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVTargetObjectFile.h | 26 |
15 files changed, 1033 insertions, 930 deletions
diff --git a/backend/include/rcc/CodeGen/AsmPrinter.h b/backend/include/rcc/CodeGen/AsmPrinter.h index 82bad7d..4145093 100644 --- a/backend/include/rcc/CodeGen/AsmPrinter.h +++ b/backend/include/rcc/CodeGen/AsmPrinter.h @@ -3,14 +3,24 @@ #ifndef RCC_CODEGEN_ASMPRINTER_H #define RCC_CODEGEN_ASMPRINTER_H +#include "rcc/IR/DataLayout.h" #include "rcc/Support/Error.h" +#include <cstddef> +#include <cstdint> +#include <utility> + namespace rcc { class CompilationUnit; +class GlobalInitializer; class MachineFunction; +class MachineJumpTableEntry; class MachineMCContext; +struct GenericConstantData; +class TargetConstant; class TargetDescription; +class TargetLoweringObjectFile; class TargetMCInstLowering; namespace mc { @@ -25,31 +35,56 @@ struct AsmPrinterOptions final { bool VerifyInput = true; }; -class AsmPrinter final { +class AsmPrinter { public: AsmPrinter(const TargetDescription &Target, const TargetMCInstLowering &Lowering, + const TargetLoweringObjectFile &ObjectFile, DataLayout Layout, MachineMCContext &Context, mc::MCStreamer &Streamer, AsmPrinterOptions Options = {}) noexcept - : Target(&Target), Lowering(&Lowering), Context(&Context), Streamer(&Streamer), - Options(Options) {} + : Target(&Target), Lowering(&Lowering), ObjectFile(&ObjectFile), Layout(std::move(Layout)), + Context(&Context), Streamer(&Streamer), Options(Options) {} + virtual ~AsmPrinter() = default; [[nodiscard]] Expected<void> emitCompilationUnit(const CompilationUnit &Unit); [[nodiscard]] Expected<void> emitFunction(const MachineFunction &Function); +protected: + [[nodiscard]] virtual Expected<void> emitStartOfAsmFile(); + [[nodiscard]] virtual Expected<void> emitEndOfAsmFile(); + [[nodiscard]] virtual Expected<void> emitTargetConstant(const TargetConstant &Constant); + [[nodiscard]] virtual Expected<void> emitTargetJumpTable(const MachineFunction &Function, + const MachineJumpTableEntry &Entry); + + [[nodiscard]] MachineMCContext &machineContext() const noexcept { + return *Context; + } + + [[nodiscard]] mc::MCStreamer &streamer() const noexcept { + return *Streamer; + } + private: [[nodiscard]] Expected<void> verifyFunction(const MachineFunction &Function) const; [[nodiscard]] Expected<void> emitGlobalObjects(const CompilationUnit &Unit); + [[nodiscard]] Expected<void> emitFunctionImpl(const MachineFunction &Function); [[nodiscard]] Expected<void> emitFunctionBody(const MachineFunction &Function); [[nodiscard]] Expected<void> emitConstantPool(const MachineFunction &Function); [[nodiscard]] Expected<void> emitJumpTables(const MachineFunction &Function); + [[nodiscard]] Expected<void> emitGenericConstant(const GenericConstantData &Constant, + std::uint64_t AllocationSize, + std::size_t Depth = 0); + [[nodiscard]] Expected<void> emitGlobalInitializer(const GlobalInitializer &Initializer, + std::size_t Depth = 0); const TargetDescription *Target; const TargetMCInstLowering *Lowering; + const TargetLoweringObjectFile *ObjectFile; + DataLayout Layout; MachineMCContext *Context; mc::MCStreamer *Streamer; AsmPrinterOptions Options; - bool UnitEmissionStarted = false; - bool UnitEmissionFinished = false; + bool EmissionStarted = false; + bool EmissionFinished = false; }; } // namespace rcc diff --git a/backend/include/rcc/CodeGen/TargetLoweringObjectFile.h b/backend/include/rcc/CodeGen/TargetLoweringObjectFile.h new file mode 100644 index 0000000..f415e4f --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetLoweringObjectFile.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETLOWERINGOBJECTFILE_H +#define RCC_CODEGEN_TARGETLOWERINGOBJECTFILE_H + +#include "rcc/MC/MCSection.h" +#include "rcc/Support/Error.h" + +namespace rcc { + +struct GlobalObjectDesc; +class MachineConstantPoolEntry; +class MachineFunction; +class MachineJumpTableEntry; +class MachineMCContext; + +class TargetLoweringObjectFile { +public: + virtual ~TargetLoweringObjectFile() = default; + + [[nodiscard]] virtual Expected<mc::MCSectionDesc> + textSection(const MachineFunction &Function) const = 0; + [[nodiscard]] virtual Expected<mc::MCSectionDesc> + globalSection(const GlobalObjectDesc &Object, MachineMCContext &Context) const = 0; + [[nodiscard]] virtual Expected<mc::MCSectionDesc> + constantPoolSection(const MachineFunction &Function, + const MachineConstantPoolEntry &Entry) const = 0; + [[nodiscard]] virtual Expected<mc::MCSectionDesc> + jumpTableSection(const MachineFunction &Function, const MachineJumpTableEntry &Entry) const = 0; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETLOWERINGOBJECTFILE_H diff --git a/backend/include/rcc/CodeGen/TargetMCInstLowering.h b/backend/include/rcc/CodeGen/TargetMCInstLowering.h index 875d2dd..eacb48e 100644 --- a/backend/include/rcc/CodeGen/TargetMCInstLowering.h +++ b/backend/include/rcc/CodeGen/TargetMCInstLowering.h @@ -3,23 +3,16 @@ #ifndef RCC_CODEGEN_TARGETMCINSTLOWERING_H #define RCC_CODEGEN_TARGETMCINSTLOWERING_H +#include "rcc/MC/MCExpr.h" #include "rcc/MC/MCInst.h" #include "rcc/MC/MCInstPrinter.h" -#include "rcc/MC/MCSection.h" #include "rcc/MC/MCStreamer.h" #include "rcc/Support/Error.h" namespace rcc { -class MachineConstantPoolEntry; -class GlobalInitializer; -struct GlobalObjectDesc; -class MachineFunction; class MachineInstr; -class MachineJumpTableEntry; class MachineMCContext; -struct GenericConstantData; -class TargetConstant; class TargetMCInstLowering { public: @@ -30,38 +23,12 @@ public: [[nodiscard]] virtual Expected<mc::MCInst> lowerInstruction(const MachineInstr &Instruction, MachineMCContext &Context) const = 0; + [[nodiscard]] virtual Expected<mc::MCExprRef> + lowerSymbolExpression(mc::MCExprRef Expression, std::uint32_t TargetFlags) const = 0; + [[nodiscard]] virtual Expected<void> emitTerminalInstruction(const MachineInstr &Instruction, MachineMCContext &Context, mc::MCStreamer &Streamer) const; - - [[nodiscard]] virtual Expected<mc::MCSectionDesc> - textSection(const MachineFunction &Function) const = 0; - [[nodiscard]] virtual Expected<mc::MCSectionDesc> - globalSection(const GlobalObjectDesc &Object, MachineMCContext &Context) const = 0; - [[nodiscard]] virtual Expected<mc::MCSectionDesc> - constantPoolSection(const MachineFunction &Function, - const MachineConstantPoolEntry &Entry) const = 0; - [[nodiscard]] virtual Expected<mc::MCSectionDesc> - jumpTableSection(const MachineFunction &Function, const MachineJumpTableEntry &Entry) const = 0; - - [[nodiscard]] virtual Expected<void> emitFileHeader(MachineMCContext &Context, - mc::MCStreamer &Streamer) const; - [[nodiscard]] virtual Expected<void> emitFileTrailer(MachineMCContext &Context, - mc::MCStreamer &Streamer) const; - [[nodiscard]] virtual Expected<void> emitGenericConstant(const GenericConstantData &Constant, - std::uint64_t Size, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const; - [[nodiscard]] virtual Expected<void> emitGlobalInitializer(const GlobalInitializer &Initializer, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const; - [[nodiscard]] virtual Expected<void> emitTargetConstant(const TargetConstant &Constant, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const; - [[nodiscard]] virtual Expected<void> emitTargetJumpTable(const MachineFunction &Function, - const MachineJumpTableEntry &Entry, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const; }; } // namespace rcc diff --git a/backend/include/rcc/Target/RISCV/RISCVMCTargetLowering.h b/backend/include/rcc/Target/RISCV/RISCVMCTargetLowering.h index fd620ad..3713bb5 100644 --- a/backend/include/rcc/Target/RISCV/RISCVMCTargetLowering.h +++ b/backend/include/rcc/Target/RISCV/RISCVMCTargetLowering.h @@ -19,39 +19,12 @@ public: [[nodiscard]] Expected<mc::MCInst> lowerInstruction(const MachineInstr &Instruction, MachineMCContext &Context) const override; + [[nodiscard]] Expected<mc::MCExprRef> + lowerSymbolExpression(mc::MCExprRef Expression, std::uint32_t TargetFlags) const override; [[nodiscard]] Expected<void> emitTerminalInstruction(const MachineInstr &Instruction, MachineMCContext &Context, mc::MCStreamer &Streamer) const override; - [[nodiscard]] Expected<mc::MCSectionDesc> - textSection(const MachineFunction &Function) const override; - [[nodiscard]] Expected<mc::MCSectionDesc> globalSection(const GlobalObjectDesc &Object, - MachineMCContext &Context) const override; - [[nodiscard]] Expected<mc::MCSectionDesc> - constantPoolSection(const MachineFunction &Function, - const MachineConstantPoolEntry &Entry) const override; - [[nodiscard]] Expected<mc::MCSectionDesc> - jumpTableSection(const MachineFunction &Function, - const MachineJumpTableEntry &Entry) const override; - - [[nodiscard]] Expected<void> emitFileHeader(MachineMCContext &Context, - mc::MCStreamer &Streamer) const override; - [[nodiscard]] Expected<void> emitFileTrailer(MachineMCContext &Context, - mc::MCStreamer &Streamer) const override; - [[nodiscard]] Expected<void> emitGenericConstant(const GenericConstantData &Constant, - std::uint64_t Size, MachineMCContext &Context, - mc::MCStreamer &Streamer) const override; - [[nodiscard]] Expected<void> emitGlobalInitializer(const GlobalInitializer &Initializer, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const override; - [[nodiscard]] Expected<void> emitTargetConstant(const TargetConstant &Constant, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const override; - [[nodiscard]] Expected<void> emitTargetJumpTable(const MachineFunction &Function, - const MachineJumpTableEntry &Entry, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const override; - private: const TargetDescription *Target; RISCVInstPrinter Printer; diff --git a/backend/lib/AsmPrinter/AsmPrinter.cpp b/backend/lib/AsmPrinter/AsmPrinter.cpp index eb21213..378d7fa 100644 --- a/backend/lib/AsmPrinter/AsmPrinter.cpp +++ b/backend/lib/AsmPrinter/AsmPrinter.cpp @@ -17,6 +17,7 @@ #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" @@ -26,11 +27,17 @@ #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 { @@ -246,8 +253,382 @@ lowerCFIInstruction(const MachineCFIInstruction &Instruction, MachineMCContext & : 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, @@ -275,16 +656,16 @@ Expected<void> AsmPrinter::verifyFunction(const MachineFunction &Function) const } Expected<void> AsmPrinter::emitCompilationUnit(const CompilationUnit &Unit) { - if (UnitEmissionStarted) { + if (EmissionStarted) { return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "a compilation unit can only be emitted once by an AsmPrinter"); + "an AsmPrinter can only emit one compilation unit or function"); } - UnitEmissionStarted = true; + EmissionStarted = true; auto Initialized = Context->initialize(Unit); if (!Initialized) return std::unexpected(Initialized.error()); - auto Header = Lowering->emitFileHeader(*Context, *Streamer); + auto Header = emitStartOfAsmFile(); if (!Header) return std::unexpected(Header.error()); @@ -298,18 +679,18 @@ Expected<void> AsmPrinter::emitCompilationUnit(const CompilationUnit &Unit) { return makeUnexpected(ErrorCode::InternalError, "compilation unit contains a null function during assembly emission"); } - auto Emitted = emitFunction(*Function); + auto Emitted = emitFunctionImpl(*Function); if (!Emitted) return std::unexpected(Emitted.error()); } - auto Trailer = Lowering->emitFileTrailer(*Context, *Streamer); + auto Trailer = emitEndOfAsmFile(); if (!Trailer) return std::unexpected(Trailer.error()); auto Finished = Streamer->finish(); if (!Finished) return std::unexpected(Finished.error()); - UnitEmissionFinished = true; + EmissionFinished = true; return {}; } @@ -337,7 +718,7 @@ Expected<void> AsmPrinter::emitGlobalObjects(const CompilationUnit &Unit) { continue; } - auto Description = Lowering->globalSection(*Object, *Context); + auto Description = ObjectFile->globalSection(*Object, *Context); if (!Description) return std::unexpected(Description.error()); auto Section = Context->context().getOrCreateSection(std::move(*Description)); @@ -356,7 +737,7 @@ Expected<void> AsmPrinter::emitGlobalObjects(const CompilationUnit &Unit) { return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "defined global object has no initializer"); } - auto Initializer = Lowering->emitGlobalInitializer(*Object->Initializer, *Context, *Streamer); + auto Initializer = emitGlobalInitializer(*Object->Initializer); if (!Initializer) return std::unexpected(Initializer.error()); auto Size = Streamer->emitSymbolSize( @@ -368,7 +749,29 @@ Expected<void> AsmPrinter::emitGlobalObjects(const CompilationUnit &Unit) { } Expected<void> AsmPrinter::emitFunction(const MachineFunction &Function) { - if (UnitEmissionFinished) { + 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"); } @@ -379,7 +782,7 @@ Expected<void> AsmPrinter::emitFunction(const MachineFunction &Function) { if (!Initialized) return std::unexpected(Initialized.error()); - auto SectionDescription = Lowering->textSection(Function); + auto SectionDescription = ObjectFile->textSection(Function); if (!SectionDescription) return std::unexpected(SectionDescription.error()); auto Section = Context->context().getOrCreateSection(std::move(*SectionDescription)); @@ -549,7 +952,7 @@ Expected<void> AsmPrinter::emitConstantPool(const MachineFunction &Function) { const MachineConstantPoolEntryDesc *Description = Entry.description(); if (Description == nullptr) continue; - auto SectionDescription = Lowering->constantPoolSection(Function, Entry); + auto SectionDescription = ObjectFile->constantPoolSection(Function, Entry); if (!SectionDescription) return std::unexpected(SectionDescription.error()); auto Section = Context->context().getOrCreateSection(std::move(*SectionDescription)); @@ -571,9 +974,9 @@ Expected<void> AsmPrinter::emitConstantPool(const MachineFunction &Function) { Expected<void> Emitted; if (const auto *Generic = std::get_if<GenericConstantData>(&Description->Value)) { - Emitted = Lowering->emitGenericConstant(*Generic, Description->Size, *Context, *Streamer); + Emitted = emitGenericConstant(*Generic, Description->Size); } else if (const auto *TargetValue = std::get_if<TargetConstant>(&Description->Value)) { - Emitted = Lowering->emitTargetConstant(*TargetValue, *Context, *Streamer); + Emitted = emitTargetConstant(*TargetValue); } else if (const auto *Difference = std::get_if<SymbolDifferenceConstant>(&Description->Value)) { if (!validValueSize(Description->Size)) { @@ -609,7 +1012,7 @@ Expected<void> AsmPrinter::emitConstantPool(const MachineFunction &Function) { Streamer->emitValue(std::move(*Expression), static_cast<std::uint8_t>(Description->Size)); } else if (const auto *TargetExpression = std::get_if<TargetExpressionConstant>(&Description->Value)) { - Emitted = Lowering->emitTargetConstant(TargetExpression->Expression, *Context, *Streamer); + Emitted = emitTargetConstant(TargetExpression->Expression); } else { return makeUnexpected(ErrorCode::InternalError, "constant-pool value has an invalid kind"); } @@ -624,7 +1027,7 @@ Expected<void> AsmPrinter::emitJumpTables(const MachineFunction &Function) { const MachineJumpTableDesc *Description = Entry.description(); if (Description == nullptr) continue; - auto SectionDescription = Lowering->jumpTableSection(Function, Entry); + auto SectionDescription = ObjectFile->jumpTableSection(Function, Entry); if (!SectionDescription) return std::unexpected(SectionDescription.error()); auto Section = Context->context().getOrCreateSection(std::move(*SectionDescription)); @@ -647,7 +1050,7 @@ Expected<void> AsmPrinter::emitJumpTables(const MachineFunction &Function) { if (Description->Encoding != JumpTableEntryEncoding::BlockAddress && Description->Encoding != JumpTableEntryEncoding::LabelDifference32 && Description->Encoding != JumpTableEntryEncoding::LabelDifference64) { - auto TargetTable = Lowering->emitTargetJumpTable(Function, Entry, *Context, *Streamer); + auto TargetTable = emitTargetJumpTable(Function, Entry); if (!TargetTable) return std::unexpected(TargetTable.error()); continue; diff --git a/backend/lib/AsmPrinter/CMakeLists.txt b/backend/lib/AsmPrinter/CMakeLists.txt index 6979a71..4b6a711 100644 --- a/backend/lib/AsmPrinter/CMakeLists.txt +++ b/backend/lib/AsmPrinter/CMakeLists.txt @@ -10,6 +10,7 @@ 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" ) diff --git a/backend/lib/AsmPrinter/TargetMCInstLowering.cpp b/backend/lib/AsmPrinter/TargetMCInstLowering.cpp index c171034..898ca2e 100644 --- a/backend/lib/AsmPrinter/TargetMCInstLowering.cpp +++ b/backend/lib/AsmPrinter/TargetMCInstLowering.cpp @@ -11,39 +11,4 @@ Expected<void> TargetMCInstLowering::emitTerminalInstruction(const MachineInstr "target does not support this terminal emission instruction"); } -Expected<void> TargetMCInstLowering::emitFileHeader(MachineMCContext &, mc::MCStreamer &) const { - return {}; -} - -Expected<void> TargetMCInstLowering::emitFileTrailer(MachineMCContext &, mc::MCStreamer &) const { - return {}; -} - -Expected<void> TargetMCInstLowering::emitGenericConstant(const GenericConstantData &, std::uint64_t, - MachineMCContext &, - mc::MCStreamer &) const { - return makeUnexpected(ErrorCode::Unsupported, - "target does not support assembly emission for generic constants"); -} - -Expected<void> TargetMCInstLowering::emitGlobalInitializer(const GlobalInitializer &, - MachineMCContext &, - mc::MCStreamer &) const { - return makeUnexpected(ErrorCode::Unsupported, - "target does not support assembly emission for global initializers"); -} - -Expected<void> TargetMCInstLowering::emitTargetConstant(const TargetConstant &, MachineMCContext &, - mc::MCStreamer &) const { - return makeUnexpected(ErrorCode::Unsupported, - "target does not support assembly emission for this target constant"); -} - -Expected<void> TargetMCInstLowering::emitTargetJumpTable(const MachineFunction &, - const MachineJumpTableEntry &, - MachineMCContext &, - mc::MCStreamer &) const { - return makeUnexpected(ErrorCode::Unsupported, "target does not support this jump-table encoding"); -} - } // namespace rcc diff --git a/backend/lib/Target/RISCV/CMakeLists.txt b/backend/lib/Target/RISCV/CMakeLists.txt index 9ce09cf..aca8ad4 100644 --- a/backend/lib/Target/RISCV/CMakeLists.txt +++ b/backend/lib/Target/RISCV/CMakeLists.txt @@ -112,6 +112,7 @@ add_library( GISel/RISCVLegalizerInfo.cpp GISel/RISCVRegisterBankInfo.cpp RISCVInstrInfo.cpp + RISCVAsmPrinter.cpp RISCVAssemblyPrinter.cpp RISCVFrameLowering.cpp RISCVFinalizeISel.cpp @@ -139,6 +140,7 @@ add_library( RISCVSubtarget.cpp RISCVTarget.cpp RISCVTargetConstant.cpp + RISCVTargetObjectFile.cpp "${RCCBackendRISCVGeneratedRegisterHeader}" "${RCCBackendRISCVGeneratedInstructionHeader}" "${RCCBackendRISCVGeneratedTables}" @@ -204,6 +206,8 @@ rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/GISel/RISCVRegisterBankInfo.h" "${CMAKE_CURRENT_SOURCE_DIR}/RISCV.h" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVInstrInfo.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVAsmPrinter.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVAsmPrinter.h" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVAssemblyPrinter.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVFrameLowering.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVFinalizeISel.cpp" @@ -229,6 +233,8 @@ rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/RISCVSubtarget.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTarget.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetConstant.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetObjectFile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetObjectFile.h" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVInstrInfo.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVAssemblyPrinter.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVInstPrinter.h" diff --git a/backend/lib/Target/RISCV/RISCVAsmPrinter.cpp b/backend/lib/Target/RISCV/RISCVAsmPrinter.cpp new file mode 100644 index 0000000..095151c --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "RISCVAsmPrinter.h" + +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineJumpTableInfo.h" +#include "rcc/CodeGen/MachineMCContext.h" +#include "rcc/CodeGen/TargetConstant.h" +#include "rcc/MC/MCContext.h" +#include "rcc/MC/MCExpr.h" +#include "rcc/MC/MCStreamer.h" +#include "rcc/MC/MCSymbol.h" +#include "rcc/Target/RISCV/RISCVTargetRelocationKinds.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <ranges> +#include <span> +#include <utility> +#include <vector> + +namespace rcc::riscv { +namespace { + +[[nodiscard]] Expected<std::uint8_t> targetRelocationSize(TargetRelocationKindID Kind) { + if (Kind == targetRelocationKind(RISCVTargetRelocationKind::Absolute32)) + return 4; + if (Kind == targetRelocationKind(RISCVTargetRelocationKind::Absolute64)) + return 8; + return makeUnexpected(ErrorCode::Unsupported, + "RV64GC target constant has an unknown relocation kind"); +} + +[[nodiscard]] Expected<mc::MCExprRef> addOffset(mc::MCExprRef Expression, std::int64_t Offset) { + if (Offset == 0) + return Expression; + auto Result = mc::MCExpr::binary(mc::MCBinaryOperator::Add, std::move(Expression), + mc::MCExpr::constant(Offset)); + if (!Result) + return std::unexpected(Result.error()); + return *Result; +} + +[[nodiscard]] Expected<void> emitBytes(std::span<const std::byte> Bytes, mc::MCStreamer &Streamer) { + if (Bytes.empty()) + return {}; + std::vector<std::uint8_t> Storage; + Storage.reserve(Bytes.size()); + for (const std::byte Byte : Bytes) + Storage.push_back(std::to_integer<std::uint8_t>(Byte)); + return Streamer.emitBytes(Storage); +} + +} // namespace + +Expected<void> RISCVAsmPrinter::emitStartOfAsmFile() { + auto Attribute = streamer().emitDirective("attribute", "arch, \"rv64gc\""); + if (!Attribute) + return std::unexpected(Attribute.error()); + return streamer().emitDirective("attribute", "stack_align, 16"); +} + +Expected<void> RISCVAsmPrinter::emitEndOfAsmFile() { + mc::MCSectionDesc Note{ + .Name = ".note.GNU-stack", + .Kind = mc::MCSectionKind::Custom, + .Flags = {}, + .Alignment = 1, + .EntrySize = 0, + .Group = {}, + .UniqueID = std::nullopt, + }; + auto Section = machineContext().context().getOrCreateSection(std::move(Note)); + if (!Section) + return std::unexpected(Section.error()); + return streamer().switchSection(std::move(*Section)); +} + +Expected<void> RISCVAsmPrinter::emitTargetConstant(const TargetConstant &Constant) { + auto Emission = Constant.emit(); + if (!Emission) + return std::unexpected(Emission.error()); + auto DeclaredKind = Constant.relocationKind(); + if (!DeclaredKind) + return std::unexpected(DeclaredKind.error()); + if (Emission->Fixups.empty() != !DeclaredKind->has_value()) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "RV64GC target constant relocation declaration is inconsistent"); + } + if (Emission->Fixups.empty()) + return emitBytes(Emission->Bytes, streamer()); + + std::vector<const TargetConstantFixup *> Fixups; + Fixups.reserve(Emission->Fixups.size()); + for (const TargetConstantFixup &Fixup : Emission->Fixups) { + if (Fixup.Kind != **DeclaredKind) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "RV64GC target constant contains mixed relocation kinds"); + } + Fixups.push_back(&Fixup); + } + std::ranges::sort(Fixups, {}, [](const TargetConstantFixup *Fixup) { return Fixup->Offset; }); + + std::uint64_t Cursor = 0; + for (const TargetConstantFixup *Fixup : Fixups) { + auto Size = targetRelocationSize(Fixup->Kind); + if (!Size) + return std::unexpected(Size.error()); + if (Fixup->Offset < Cursor || Fixup->Offset > Emission->Bytes.size() || + *Size > Emission->Bytes.size() - static_cast<std::size_t>(Fixup->Offset)) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "RV64GC target constant relocation storage is invalid"); + } + auto RawBytes = emitBytes(std::span<const std::byte>(Emission->Bytes) + .subspan(static_cast<std::size_t>(Cursor), + static_cast<std::size_t>(Fixup->Offset - Cursor)), + streamer()); + if (!RawBytes) + return std::unexpected(RawBytes.error()); + + auto Symbol = machineContext().symbol(Fixup->Symbol); + if (!Symbol) + return std::unexpected(Symbol.error()); + auto Expression = mc::MCExpr::symbol(std::move(*Symbol)); + if (!Expression) + return std::unexpected(Expression.error()); + auto WithAddend = addOffset(std::move(*Expression), Fixup->Addend); + if (!WithAddend) + return std::unexpected(WithAddend.error()); + auto Value = streamer().emitValue(std::move(*WithAddend), *Size); + if (!Value) + return std::unexpected(Value.error()); + Cursor = Fixup->Offset + *Size; + } + return emitBytes( + std::span<const std::byte>(Emission->Bytes).subspan(static_cast<std::size_t>(Cursor)), + streamer()); +} + +Expected<void> RISCVAsmPrinter::emitTargetJumpTable(const MachineFunction &Function, + const MachineJumpTableEntry &Entry) { + const MachineJumpTableDesc *Description = Entry.description(); + if (Description == nullptr) + return makeUnexpected(ErrorCode::InvalidArgument, "jump-table entry is not live"); + if (Description->Encoding == JumpTableEntryEncoding::Target) { + return makeUnexpected(ErrorCode::Unsupported, + "RV64GC jump table has an unknown target encoding"); + } + + mc::MCSymbolRef BaseSymbol; + if (Description->Encoding == JumpTableEntryEncoding::GPRelative32BlockAddress || + Description->Encoding == JumpTableEntryEncoding::GPRelative64BlockAddress) { + auto GlobalPointer = machineContext().symbol("__global_pointer$"); + if (!GlobalPointer) + return std::unexpected(GlobalPointer.error()); + BaseSymbol = std::move(*GlobalPointer); + } else if (Description->Encoding == JumpTableEntryEncoding::Custom32) { + BaseSymbol = machineContext().jumpTableSymbol(Function, Entry.index()); + } + + for (const MachineBasicBlock *TargetBlock : Description->Targets) { + if (TargetBlock == nullptr) + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "jump table has a null target"); + const mc::MCSymbolRef TargetSymbol = machineContext().blockSymbol(*TargetBlock); + if (!TargetSymbol) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "jump-table target has no MC symbol"); + } + auto Expression = mc::MCExpr::symbol(TargetSymbol); + if (!Expression) + return std::unexpected(Expression.error()); + if (BaseSymbol) { + auto Base = mc::MCExpr::symbol(BaseSymbol); + if (!Base) + return std::unexpected(Base.error()); + Expression = mc::MCExpr::binary(mc::MCBinaryOperator::Subtract, std::move(*Expression), + std::move(*Base)); + 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::riscv diff --git a/backend/lib/Target/RISCV/RISCVAsmPrinter.h b/backend/lib/Target/RISCV/RISCVAsmPrinter.h new file mode 100644 index 0000000..f74e569 --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVAsmPrinter.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVASMPRINTER_H +#define RCC_TARGET_RISCV_RISCVASMPRINTER_H + +#include "rcc/CodeGen/AsmPrinter.h" + +namespace rcc::riscv { + +class RISCVAsmPrinter final : public AsmPrinter { +public: + RISCVAsmPrinter(const TargetDescription &Target, const TargetMCInstLowering &Lowering, + const TargetLoweringObjectFile &ObjectFile, DataLayout Layout, + MachineMCContext &Context, mc::MCStreamer &Streamer, + AsmPrinterOptions Options = {}) noexcept + : AsmPrinter(Target, Lowering, ObjectFile, std::move(Layout), Context, Streamer, Options) {} + +private: + [[nodiscard]] Expected<void> emitStartOfAsmFile() override; + [[nodiscard]] Expected<void> emitEndOfAsmFile() override; + [[nodiscard]] Expected<void> emitTargetConstant(const TargetConstant &Constant) override; + [[nodiscard]] Expected<void> emitTargetJumpTable(const MachineFunction &Function, + const MachineJumpTableEntry &Entry) override; +}; + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVASMPRINTER_H diff --git a/backend/lib/Target/RISCV/RISCVAssemblyPrinter.cpp b/backend/lib/Target/RISCV/RISCVAssemblyPrinter.cpp index 7661c61..85f4617 100644 --- a/backend/lib/Target/RISCV/RISCVAssemblyPrinter.cpp +++ b/backend/lib/Target/RISCV/RISCVAssemblyPrinter.cpp @@ -2,15 +2,20 @@ #include "rcc/Target/RISCV/RISCVAssemblyPrinter.h" +#include "RISCVAsmPrinter.h" +#include "RISCVTargetObjectFile.h" #include "rcc/CodeGen/AsmPrinter.h" #include "rcc/CodeGen/CompilationUnit.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineMCContext.h" +#include "rcc/IR/DataLayout.h" #include "rcc/MC/MCContext.h" #include "rcc/MC/MCStreamer.h" #include "rcc/Target/RISCV/RISCVMCTargetLowering.h" +#include "rcc/Target/RISCV/RISCVTarget.h" #include <string> +#include <utility> namespace rcc::riscv { namespace { @@ -32,8 +37,13 @@ Expected<std::string> emitAssembly(const CompilationUnit &Unit, const TargetDesc mc::MCContext MCContext; MachineMCContext MachineContext(MCContext); RISCVMCTargetLowering Lowering(Target); + RISCVELFTargetObjectFile ObjectFile; + auto Layout = DataLayout::parse(TargetDataLayout); + if (!Layout) + return std::unexpected(Layout.error()); mc::AsmTextStreamer Streamer(Lowering.instructionPrinter()); - AsmPrinter Printer(Target, Lowering, MachineContext, Streamer, printerOptions(Options)); + RISCVAsmPrinter Printer(Target, Lowering, ObjectFile, std::move(*Layout), MachineContext, + Streamer, printerOptions(Options)); auto Emitted = Printer.emitCompilationUnit(Unit); if (!Emitted) return std::unexpected(Emitted.error()); @@ -45,21 +55,16 @@ Expected<std::string> emitAssembly(const MachineFunction &Function, const Target mc::MCContext MCContext; MachineMCContext MachineContext(MCContext); RISCVMCTargetLowering Lowering(Target); + RISCVELFTargetObjectFile ObjectFile; + auto Layout = DataLayout::parse(TargetDataLayout); + if (!Layout) + return std::unexpected(Layout.error()); mc::AsmTextStreamer Streamer(Lowering.instructionPrinter()); - AsmPrinter Printer(Target, Lowering, MachineContext, Streamer, printerOptions(Options)); - - auto Header = Lowering.emitFileHeader(MachineContext, Streamer); - if (!Header) - return std::unexpected(Header.error()); + RISCVAsmPrinter Printer(Target, Lowering, ObjectFile, std::move(*Layout), MachineContext, + Streamer, printerOptions(Options)); auto Emitted = Printer.emitFunction(Function); if (!Emitted) return std::unexpected(Emitted.error()); - auto Trailer = Lowering.emitFileTrailer(MachineContext, Streamer); - if (!Trailer) - return std::unexpected(Trailer.error()); - auto Finished = Streamer.finish(); - if (!Finished) - return std::unexpected(Finished.error()); return Streamer.takeOutput(); } diff --git a/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp b/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp index 85c06ad..065bd36 100644 --- a/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp +++ b/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp @@ -3,40 +3,26 @@ #include "rcc/Target/RISCV/RISCVMCTargetLowering.h" #include "rcc/CodeGen/GenericOpcodes.h" -#include "rcc/CodeGen/GlobalObject.h" #include "rcc/CodeGen/InstructionDescriptor.h" -#include "rcc/CodeGen/MachineBasicBlock.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/Register.h" -#include "rcc/CodeGen/TargetConstant.h" #include "rcc/IR/Constants.h" -#include "rcc/IR/DataLayout.h" -#include "rcc/MC/MCContext.h" #include "rcc/MC/MCExpr.h" #include "rcc/MC/MCInst.h" -#include "rcc/MC/MCSection.h" #include "rcc/MC/MCStreamer.h" #include "rcc/MC/MCSymbol.h" #include "rcc/Target/RISCV/RISCVTarget.h" -#include "rcc/Target/RISCV/RISCVTargetRelocationKinds.h" -#include <algorithm> #include <bit> #include <cstddef> #include <cstdint> -#include <limits> #include <optional> #include <ranges> -#include <span> -#include <string> #include <string_view> #include <utility> -#include <vector> namespace rcc::riscv { namespace { @@ -54,15 +40,6 @@ constexpr std::uint32_t KnownTargetFlags = CallTargetFlag | PCRelativeHighTarget CopyF64TargetFlag | AbsoluteHighTargetFlag | AbsoluteLowTargetFlag | GOTPCRelativeHighTargetFlag; -[[nodiscard]] Expected<std::uint8_t> targetRelocationSize(TargetRelocationKindID Kind) { - if (Kind == targetRelocationKind(RISCVTargetRelocationKind::Absolute32)) - return 4; - if (Kind == targetRelocationKind(RISCVTargetRelocationKind::Absolute64)) - return 8; - return makeUnexpected(ErrorCode::Unsupported, - "RV64GC target constant has an unknown relocation kind"); -} - [[nodiscard]] Expected<mc::MCExprRef> addOffset(mc::MCExprRef Expression, std::int64_t Offset) { if (Offset == 0) return Expression; @@ -276,553 +253,6 @@ constexpr std::uint32_t KnownTargetFlags = CallTargetFlag | PCRelativeHighTarget return std::nullopt; } -[[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, - "RV64GC 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, - "RV64GC 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> emitPadding(std::uint64_t Size, mc::MCStreamer &Streamer) { - return Size == 0 ? Expected<void>{} : Streamer.emitZeroFill(Size); -} - -[[nodiscard]] Expected<void> -emitGenericConstantImpl(const GenericConstantData &Constant, const DataLayout &Layout, - std::uint64_t AllocationSize, MachineMCContext &Context, - mc::MCStreamer &Streamer, std::size_t Depth = 0) { - if (Depth > 1024) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "generic constant exceeds its emission nesting limit"); - } - switch (Constant.Kind) { - case GenericConstantKind::Integer: { - if (!Constant.IntegerValue) - return makeUnexpected(ErrorCode::InternalError, "integer constant has no value"); - const auto Bytes = bytesFromWords(Constant.IntegerValue->words(), AllocationSize); - return Streamer.emitBytes(Bytes); - } - case GenericConstantKind::Floating: { - if (!Constant.FloatingValue) - return makeUnexpected(ErrorCode::InternalError, "floating constant has no value"); - const auto Bytes = bytesFromWords(Constant.FloatingValue->words(), AllocationSize); - return Streamer.emitBytes(Bytes); - } - case GenericConstantKind::ByteString: { - if (Constant.ByteValue.size() != AllocationSize) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "byte-string constant size does not match its RV64GC 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 (std::size_t Index = 0; Index < Constant.Elements.size(); ++Index) { - auto ElementSize = Layout.getTypeAllocSize(Constant.Elements[Index].Type); - auto ElementAlignment = Layout.getABITypeAlign(Constant.Elements[Index].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 RV64GC layout"); - } - const std::uint64_t ElementOffset = (Offset + AlignmentMask) & ~AlignmentMask; - if (ElementOffset > AllocationSize || *ElementSize > AllocationSize - ElementOffset) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "aggregate constant exceeds its RV64GC layout"); - } - auto Padding = emitPadding(ElementOffset - Offset, Streamer); - if (!Padding) - return std::unexpected(Padding.error()); - auto Element = emitGenericConstantImpl(Constant.Elements[Index], Layout, *ElementSize, - Context, Streamer, Depth + 1); - if (!Element) - return std::unexpected(Element.error()); - Offset = ElementOffset + *ElementSize; - } - if (Offset > AllocationSize) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "aggregate constant exceeds its RV64GC layout"); - } - return emitPadding(AllocationSize - Offset, Streamer); - } - case GenericConstantKind::GlobalAddress: - case GenericConstantKind::BlockAddress: - case GenericConstantKind::DSOLocalEquivalent: - case GenericConstantKind::NoCFI: - case GenericConstantKind::Expression: { - if (AllocationSize != 1 && AllocationSize != 2 && AllocationSize != 4 && AllocationSize != 8) { - return makeUnexpected(ErrorCode::Unsupported, - "symbolic RV64GC 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, - "RV64GC 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"); -} - -[[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 {}; -} - -[[nodiscard]] Expected<void> emitTargetBytes(std::span<const std::byte> Bytes, - mc::MCStreamer &Streamer) { - if (Bytes.empty()) - return {}; - std::vector<std::uint8_t> Storage; - Storage.reserve(Bytes.size()); - for (const std::byte Byte : Bytes) - Storage.push_back(std::to_integer<std::uint8_t>(Byte)); - return Streamer.emitBytes(Storage); -} - -[[nodiscard]] Expected<void> emitTargetConstantImpl(const TargetConstant &Constant, - MachineMCContext &Context, - mc::MCStreamer &Streamer) { - auto Emission = Constant.emit(); - if (!Emission) - return std::unexpected(Emission.error()); - auto DeclaredKind = Constant.relocationKind(); - if (!DeclaredKind) - return std::unexpected(DeclaredKind.error()); - if (Emission->Fixups.empty() != !DeclaredKind->has_value()) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "RV64GC target constant relocation declaration is inconsistent"); - } - if (Emission->Fixups.empty()) - return emitTargetBytes(Emission->Bytes, Streamer); - - std::vector<const TargetConstantFixup *> Fixups; - Fixups.reserve(Emission->Fixups.size()); - for (const TargetConstantFixup &Fixup : Emission->Fixups) { - if (Fixup.Kind != **DeclaredKind) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "RV64GC target constant contains mixed relocation kinds"); - } - Fixups.push_back(&Fixup); - } - std::ranges::sort(Fixups, {}, [](const TargetConstantFixup *Fixup) { return Fixup->Offset; }); - - std::uint64_t Cursor = 0; - for (const TargetConstantFixup *Fixup : Fixups) { - auto Size = targetRelocationSize(Fixup->Kind); - if (!Size) - return std::unexpected(Size.error()); - if (Fixup->Offset < Cursor || Fixup->Offset > Emission->Bytes.size() || - *Size > Emission->Bytes.size() - static_cast<std::size_t>(Fixup->Offset)) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "RV64GC target constant relocation storage is invalid"); - } - auto RawBytes = emitTargetBytes(std::span<const std::byte>(Emission->Bytes) - .subspan(static_cast<std::size_t>(Cursor), - static_cast<std::size_t>(Fixup->Offset - Cursor)), - Streamer); - if (!RawBytes) - return std::unexpected(RawBytes.error()); - - auto Symbol = Context.symbol(Fixup->Symbol); - if (!Symbol) - return std::unexpected(Symbol.error()); - auto Expression = mc::MCExpr::symbol(std::move(*Symbol)); - if (!Expression) - return std::unexpected(Expression.error()); - auto WithAddend = addOffset(std::move(*Expression), Fixup->Addend); - if (!WithAddend) - return std::unexpected(WithAddend.error()); - auto Value = Streamer.emitValue(std::move(*WithAddend), *Size); - if (!Value) - return std::unexpected(Value.error()); - Cursor = Fixup->Offset + *Size; - } - return emitTargetBytes( - std::span<const std::byte>(Emission->Bytes).subspan(static_cast<std::size_t>(Cursor)), - Streamer); -} - -[[nodiscard]] Expected<void> emitGlobalInitializerImpl(const GlobalInitializer &Initializer, - MachineMCContext &Context, - mc::MCStreamer &Streamer, - std::size_t Depth = 0) { - if (Depth > 1024) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "global initializer exceeds its emission nesting limit"); - } - 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 = emitGlobalInitializerImpl(*Value->Value, Context, Streamer, 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 = emitGlobalInitializerImpl(*Element, Context, Streamer, 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 = mc::MCExpr::symbol(std::move(*Symbol)); - if (!Expression) - return std::unexpected(Expression.error()); - auto WithAddend = addOffset(std::move(*Expression), Value->Addend); - if (!WithAddend) - return std::unexpected(WithAddend.error()); - auto Lowered = applyTargetFlags(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 MinuendSymbol = Context.symbol(Value->Minuend); - if (!MinuendSymbol) - return std::unexpected(MinuendSymbol.error()); - auto SubtrahendSymbol = Context.symbol(Value->Subtrahend); - if (!SubtrahendSymbol) - return std::unexpected(SubtrahendSymbol.error()); - auto Minuend = mc::MCExpr::symbol(std::move(*MinuendSymbol)); - if (!Minuend) - return std::unexpected(Minuend.error()); - auto Subtrahend = mc::MCExpr::symbol(std::move(*SubtrahendSymbol)); - if (!Subtrahend) - return std::unexpected(Subtrahend.error()); - auto Difference = mc::MCExpr::binary(mc::MCBinaryOperator::Subtract, std::move(*Minuend), - std::move(*Subtrahend)); - if (!Difference) - return std::unexpected(Difference.error()); - auto WithAddend = addOffset(std::move(*Difference), Value->Addend); - if (!WithAddend) - return std::unexpected(WithAddend.error()); - return Streamer.emitValue(std::move(*WithAddend), 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 emitTargetConstantImpl(*Value, Context, Streamer); - } - 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"); -} - -[[nodiscard]] bool initializerIsZero(const GlobalInitializer &Initializer, std::size_t Depth = 0) { - if (Depth > 1024) - return false; - switch (Initializer.kind()) { - case GlobalInitializer::Kind::Integer: { - const auto *Value = Initializer.getIf<APInt>(); - return Value != nullptr && - std::ranges::all_of(Value->words(), [](std::uint64_t Word) { return Word == 0; }); - } - case GlobalInitializer::Kind::Floating: { - const auto *Value = Initializer.getIf<APFloat>(); - return Value != nullptr && - std::ranges::all_of(Value->words(), [](std::uint64_t Word) { return Word == 0; }); - } - case GlobalInitializer::Kind::Bytes: { - const auto *Value = Initializer.getIf<std::vector<std::byte>>(); - return Value != nullptr && - std::ranges::all_of(*Value, [](std::byte Byte) { return Byte == std::byte{0}; }); - } - case GlobalInitializer::Kind::ZeroFill: - case GlobalInitializer::Kind::UndefinedStorage: - return true; - case GlobalInitializer::Kind::Repeated: { - const auto *Value = Initializer.getIf<GlobalRepeatedInitializer>(); - return Value != nullptr && Value->Value && initializerIsZero(*Value->Value, Depth + 1); - } - case GlobalInitializer::Kind::Aggregate: { - const auto *Value = Initializer.getIf<GlobalAggregateInitializer>(); - return Value != nullptr && std::ranges::all_of(Value->Elements, [Depth](const auto &Element) { - return Element && initializerIsZero(*Element, Depth + 1); - }); - } - case GlobalInitializer::Kind::AlignmentPadding: { - const auto *Value = Initializer.getIf<GlobalAlignmentPadding>(); - return Value != nullptr && Value->Fill == 0; - } - case GlobalInitializer::Kind::Target: { - const auto *Value = Initializer.getIf<TargetConstant>(); - if (Value == nullptr) - return false; - auto Emission = Value->emit(); - return Emission && Emission->Fixups.empty() && - std::ranges::all_of(Emission->Bytes, - [](std::byte Byte) { return Byte == std::byte{0}; }); - } - case GlobalInitializer::Kind::Invalid: - case GlobalInitializer::Kind::SymbolAddress: - case GlobalInitializer::Kind::SymbolDifference: - return false; - } - return false; -} - -[[nodiscard]] bool initializerHasRelocation(const GlobalInitializer &Initializer, - std::size_t Depth = 0) { - if (Depth > 1024) - return true; - switch (Initializer.kind()) { - case GlobalInitializer::Kind::SymbolAddress: - case GlobalInitializer::Kind::SymbolDifference: - return true; - case GlobalInitializer::Kind::Repeated: { - const auto *Value = Initializer.getIf<GlobalRepeatedInitializer>(); - return Value == nullptr || !Value->Value || initializerHasRelocation(*Value->Value, Depth + 1); - } - case GlobalInitializer::Kind::Aggregate: { - const auto *Value = Initializer.getIf<GlobalAggregateInitializer>(); - return Value == nullptr || std::ranges::any_of(Value->Elements, [Depth](const auto &Element) { - return !Element || initializerHasRelocation(*Element, Depth + 1); - }); - } - case GlobalInitializer::Kind::Target: { - const auto *Value = Initializer.getIf<TargetConstant>(); - if (Value == nullptr) - return true; - auto Emission = Value->emit(); - return !Emission || !Emission->Fixups.empty(); - } - case GlobalInitializer::Kind::Invalid: - return true; - case GlobalInitializer::Kind::Integer: - case GlobalInitializer::Kind::Floating: - case GlobalInitializer::Kind::Bytes: - case GlobalInitializer::Kind::ZeroFill: - case GlobalInitializer::Kind::AlignmentPadding: - case GlobalInitializer::Kind::UndefinedStorage: - return false; - } - return true; -} - -[[nodiscard]] mc::MCSectionDesc textSectionDescription() { - return mc::MCSectionDesc{ - .Name = ".text", - .Kind = mc::MCSectionKind::Text, - .Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Executable}, - .Alignment = 4, - .EntrySize = 0, - .Group = {}, - .UniqueID = std::nullopt, - }; -} - -[[nodiscard]] mc::MCSectionDesc readOnlySectionDescription() { - return mc::MCSectionDesc{ - .Name = ".rodata", - .Kind = mc::MCSectionKind::ReadOnly, - .Flags = {mc::MCSectionFlag::Alloc}, - .Alignment = 1, - .EntrySize = 0, - .Group = {}, - .UniqueID = std::nullopt, - }; -} - } // namespace Expected<mc::MCInst> RISCVMCTargetLowering::lowerInstruction(const MachineInstr &Instruction, @@ -867,6 +297,12 @@ Expected<mc::MCInst> RISCVMCTargetLowering::lowerInstruction(const MachineInstr return Result; } +Expected<mc::MCExprRef> +RISCVMCTargetLowering::lowerSymbolExpression(mc::MCExprRef Expression, + std::uint32_t TargetFlags) const { + return applyTargetFlags(std::move(Expression), TargetFlags); +} + Expected<void> RISCVMCTargetLowering::emitTerminalInstruction(const MachineInstr &Instruction, MachineMCContext &Context, mc::MCStreamer &Streamer) const { @@ -895,216 +331,4 @@ Expected<void> RISCVMCTargetLowering::emitTerminalInstruction(const MachineInstr return {}; } -Expected<mc::MCSectionDesc> RISCVMCTargetLowering::textSection(const MachineFunction &) const { - return textSectionDescription(); -} - -Expected<mc::MCSectionDesc> RISCVMCTargetLowering::globalSection(const GlobalObjectDesc &Object, - MachineMCContext &Context) const { - if (Object.AddressSpace != 0) { - return makeUnexpected(ErrorCode::Unsupported, - "RV64GC assembly does not support nonzero global address spaces"); - } - if (Object.TargetFlags != 0 || !Object.TargetAttributes.empty()) { - return makeUnexpected(ErrorCode::Unsupported, - "RV64GC assembly does not recognize this global target state"); - } - const bool IsTLS = Object.TLSModel != GlobalTLSModel::None; - const bool IsZero = Object.Initializer && initializerIsZero(*Object.Initializer); - const bool HasRelocation = Object.Initializer && initializerHasRelocation(*Object.Initializer); - - mc::MCSectionDesc Result; - Result.Alignment = Object.Align.bytes(); - if (IsTLS) { - Result.Name = IsZero ? ".tbss" : ".tdata"; - Result.Kind = IsZero ? mc::MCSectionKind::TLSBSS : mc::MCSectionKind::TLSData; - Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable, mc::MCSectionFlag::TLS}; - } else if (Object.IsConstant && HasRelocation) { - Result.Name = ".data.rel.ro"; - Result.Kind = mc::MCSectionKind::Data; - Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable}; - } else if (Object.IsConstant) { - Result = readOnlySectionDescription(); - Result.Alignment = Object.Align.bytes(); - } else if (IsZero) { - Result.Name = ".bss"; - Result.Kind = mc::MCSectionKind::BSS; - Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable}; - } else { - Result.Name = ".data"; - Result.Kind = mc::MCSectionKind::Data; - Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable}; - } - - if (Object.Section) - Result.Name = *Object.Section; - if (Object.COMDAT) { - auto Group = Context.symbol(*Object.COMDAT); - if (!Group) - return std::unexpected(Group.error()); - Result.Flags.set(mc::MCSectionFlag::Group); - Result.Group = std::move(*Group); - if (!Object.Section) - Result.Name += "." + Object.Symbol; - } - return Result; -} - -Expected<mc::MCSectionDesc> -RISCVMCTargetLowering::constantPoolSection(const MachineFunction &Function, - const MachineConstantPoolEntry &Entry) const { - const MachineConstantPoolEntryDesc *Description = Entry.description(); - if (Description == nullptr) - return makeUnexpected(ErrorCode::InvalidArgument, "constant-pool entry is not live"); - if (Description->Placement.Section == ConstantPoolSectionKind::TextAdjacent) - return textSectionDescription(); - - mc::MCSectionDesc Result = readOnlySectionDescription(); - if (Description->Placement.Section == ConstantPoolSectionKind::Data) { - Result.Name = ".data"; - Result.Kind = mc::MCSectionKind::Data; - Result.Flags.set(mc::MCSectionFlag::Writable); - } else if (Description->Placement.Section == ConstantPoolSectionKind::Target) { - return makeUnexpected(ErrorCode::Unsupported, - "RV64GC constant pool has an unknown target section"); - } else if (Description->Placement.Section == ConstantPoolSectionKind::MergeableReadOnly) { - Result.Name = ".rodata.cst" + std::to_string(Description->Size); - Result.Flags.set(mc::MCSectionFlag::Merge); - Result.EntrySize = Description->Size; - } - if (Description->Placement.UniqueSection) { - Result.Name += "." + std::to_string(Function.stableID().value()) + "." + - std::to_string(Entry.index().value()); - Result.UniqueID = static_cast<std::uint64_t>(Entry.index().value()) + 1U; - } - Result.Alignment = Description->Align.bytes(); - return Result; -} - -Expected<mc::MCSectionDesc> -RISCVMCTargetLowering::jumpTableSection(const MachineFunction &Function, - const MachineJumpTableEntry &Entry) const { - const MachineJumpTableDesc *Description = Entry.description(); - if (Description == nullptr) - return makeUnexpected(ErrorCode::InvalidArgument, "jump-table entry is not live"); - mc::MCSectionDesc Result = Description->Placement.InTextSection ? textSectionDescription() - : readOnlySectionDescription(); - if (Description->Placement.UniqueSection) { - Result.Name += "." + std::to_string(Function.stableID().value()) + ".jt." + - std::to_string(Entry.index().value()); - Result.UniqueID = static_cast<std::uint64_t>(Entry.index().value()) + 1U; - } - Result.Alignment = Description->Align.bytes(); - return Result; -} - -Expected<void> RISCVMCTargetLowering::emitFileHeader(MachineMCContext &, - mc::MCStreamer &Streamer) const { - auto Attribute = Streamer.emitDirective("attribute", "arch, \"rv64gc\""); - if (!Attribute) - return std::unexpected(Attribute.error()); - return Streamer.emitDirective("attribute", "stack_align, 16"); -} - -Expected<void> RISCVMCTargetLowering::emitFileTrailer(MachineMCContext &Context, - mc::MCStreamer &Streamer) const { - mc::MCSectionDesc Note{ - .Name = ".note.GNU-stack", - .Kind = mc::MCSectionKind::Custom, - .Flags = {}, - .Alignment = 1, - .EntrySize = 0, - .Group = {}, - .UniqueID = std::nullopt, - }; - auto Section = Context.context().getOrCreateSection(std::move(Note)); - if (!Section) - return std::unexpected(Section.error()); - return Streamer.switchSection(std::move(*Section)); -} - -Expected<void> RISCVMCTargetLowering::emitGenericConstant(const GenericConstantData &Constant, - std::uint64_t Size, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const { - auto Verification = Constant.verify(); - if (!Verification) - return std::unexpected(Verification.error()); - auto Layout = DataLayout::parse(TargetDataLayout); - if (!Layout) - return std::unexpected(Layout.error()); - auto AllocationSize = Layout->getTypeAllocSize(Constant.Type); - if (!AllocationSize) - return std::unexpected(AllocationSize.error()); - if (*AllocationSize != Size) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "generic constant size does not match the RV64GC data layout"); - } - return emitGenericConstantImpl(Constant, *Layout, *AllocationSize, Context, Streamer); -} - -Expected<void> RISCVMCTargetLowering::emitGlobalInitializer(const GlobalInitializer &Initializer, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const { - auto Verification = Initializer.verify(); - if (!Verification) - return std::unexpected(Verification.error()); - return emitGlobalInitializerImpl(Initializer, Context, Streamer); -} - -Expected<void> RISCVMCTargetLowering::emitTargetConstant(const TargetConstant &Constant, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const { - return emitTargetConstantImpl(Constant, Context, Streamer); -} - -Expected<void> RISCVMCTargetLowering::emitTargetJumpTable(const MachineFunction &Function, - const MachineJumpTableEntry &Entry, - MachineMCContext &Context, - mc::MCStreamer &Streamer) const { - const MachineJumpTableDesc *Description = Entry.description(); - if (Description == nullptr) - return makeUnexpected(ErrorCode::InvalidArgument, "jump-table entry is not live"); - if (Description->Encoding == JumpTableEntryEncoding::Target) { - return makeUnexpected(ErrorCode::Unsupported, - "RV64GC jump table has an unknown target encoding"); - } - - mc::MCSymbolRef BaseSymbol; - if (Description->Encoding == JumpTableEntryEncoding::GPRelative32BlockAddress || - Description->Encoding == JumpTableEntryEncoding::GPRelative64BlockAddress) { - auto GlobalPointer = Context.symbol("__global_pointer$"); - if (!GlobalPointer) - return std::unexpected(GlobalPointer.error()); - BaseSymbol = std::move(*GlobalPointer); - } else if (Description->Encoding == JumpTableEntryEncoding::Custom32) { - BaseSymbol = Context.jumpTableSymbol(Function, Entry.index()); - } - - 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::AssemblyLoweringFailed, - "jump-table target has no MC symbol"); - auto Expression = mc::MCExpr::symbol(TargetSymbol); - if (!Expression) - return std::unexpected(Expression.error()); - if (BaseSymbol) { - auto Base = mc::MCExpr::symbol(BaseSymbol); - if (!Base) - return std::unexpected(Base.error()); - Expression = mc::MCExpr::binary(mc::MCBinaryOperator::Subtract, std::move(*Expression), - std::move(*Base)); - 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::riscv diff --git a/backend/lib/Target/RISCV/RISCVObjectFile.cpp b/backend/lib/Target/RISCV/RISCVObjectFile.cpp index 15d4980..706c1f8 100644 --- a/backend/lib/Target/RISCV/RISCVObjectFile.cpp +++ b/backend/lib/Target/RISCV/RISCVObjectFile.cpp @@ -2,16 +2,20 @@ #include "rcc/Target/RISCV/RISCVObjectFile.h" +#include "RISCVAsmPrinter.h" +#include "RISCVTargetObjectFile.h" #include "rcc/CodeGen/AsmPrinter.h" #include "rcc/CodeGen/CompilationUnit.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineMCContext.h" +#include "rcc/IR/DataLayout.h" #include "rcc/MC/ELFObjectWriter.h" #include "rcc/MC/MCContext.h" #include "rcc/MC/MCObjectStreamer.h" #include "rcc/Target/RISCV/RISCVAsmBackend.h" #include "rcc/Target/RISCV/RISCVMCCodeEmitter.h" #include "rcc/Target/RISCV/RISCVMCTargetLowering.h" +#include "rcc/Target/RISCV/RISCVTarget.h" #include <cstdint> #include <utility> @@ -99,6 +103,7 @@ struct ObjectEmissionContext final { mc::MCContext Context; MachineMCContext MachineContext; RISCVMCTargetLowering Lowering; + RISCVELFTargetObjectFile ObjectFile; RISCVMCCodeEmitter Emitter; RISCVAsmBackend Backend; mc::ELF64LEObjectWriter Writer; @@ -114,8 +119,11 @@ Expected<std::vector<std::uint8_t>> emitObject(const CompilationUnit &Unit, ObjectEmissionContext Context(Target); if (!Context.Initialization) return std::unexpected(Context.Initialization.error()); - AsmPrinter Printer(Target, Context.Lowering, Context.MachineContext, Context.Streamer, - printerOptions(Options)); + auto Layout = DataLayout::parse(TargetDataLayout); + if (!Layout) + return std::unexpected(Layout.error()); + RISCVAsmPrinter Printer(Target, Context.Lowering, Context.ObjectFile, std::move(*Layout), + Context.MachineContext, Context.Streamer, printerOptions(Options)); auto Emitted = Printer.emitCompilationUnit(Unit); if (!Emitted) return std::unexpected(Emitted.error()); @@ -128,20 +136,14 @@ Expected<std::vector<std::uint8_t>> emitObject(const MachineFunction &Function, ObjectEmissionContext Context(Target); if (!Context.Initialization) return std::unexpected(Context.Initialization.error()); - AsmPrinter Printer(Target, Context.Lowering, Context.MachineContext, Context.Streamer, - printerOptions(Options)); - auto Header = Context.Lowering.emitFileHeader(Context.MachineContext, Context.Streamer); - if (!Header) - return std::unexpected(Header.error()); + auto Layout = DataLayout::parse(TargetDataLayout); + if (!Layout) + return std::unexpected(Layout.error()); + RISCVAsmPrinter Printer(Target, Context.Lowering, Context.ObjectFile, std::move(*Layout), + Context.MachineContext, Context.Streamer, printerOptions(Options)); auto Emitted = Printer.emitFunction(Function); if (!Emitted) return std::unexpected(Emitted.error()); - auto Trailer = Context.Lowering.emitFileTrailer(Context.MachineContext, Context.Streamer); - if (!Trailer) - return std::unexpected(Trailer.error()); - auto Finished = Context.Streamer.finish(); - if (!Finished) - return std::unexpected(Finished.error()); return Context.Streamer.takeOutput(); } diff --git a/backend/lib/Target/RISCV/RISCVTargetObjectFile.cpp b/backend/lib/Target/RISCV/RISCVTargetObjectFile.cpp new file mode 100644 index 0000000..9a8aebb --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVTargetObjectFile.cpp @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "RISCVTargetObjectFile.h" + +#include "rcc/CodeGen/GlobalObject.h" +#include "rcc/CodeGen/MachineConstantPool.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineJumpTableInfo.h" +#include "rcc/CodeGen/MachineMCContext.h" +#include "rcc/CodeGen/TargetConstant.h" +#include "rcc/MC/MCContext.h" +#include "rcc/MC/MCSymbol.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <ranges> +#include <string> + +namespace rcc::riscv { +namespace { + +[[nodiscard]] bool initializerIsZero(const GlobalInitializer &Initializer, std::size_t Depth = 0) { + if (Depth > 1024) + return false; + switch (Initializer.kind()) { + case GlobalInitializer::Kind::Integer: { + const auto *Value = Initializer.getIf<APInt>(); + return Value != nullptr && + std::ranges::all_of(Value->words(), [](std::uint64_t Word) { return Word == 0; }); + } + case GlobalInitializer::Kind::Floating: { + const auto *Value = Initializer.getIf<APFloat>(); + return Value != nullptr && + std::ranges::all_of(Value->words(), [](std::uint64_t Word) { return Word == 0; }); + } + case GlobalInitializer::Kind::Bytes: { + const auto *Value = Initializer.getIf<std::vector<std::byte>>(); + return Value != nullptr && + std::ranges::all_of(*Value, [](std::byte Byte) { return Byte == std::byte{0}; }); + } + case GlobalInitializer::Kind::ZeroFill: + case GlobalInitializer::Kind::UndefinedStorage: + return true; + case GlobalInitializer::Kind::Repeated: { + const auto *Value = Initializer.getIf<GlobalRepeatedInitializer>(); + return Value != nullptr && Value->Value && initializerIsZero(*Value->Value, Depth + 1); + } + case GlobalInitializer::Kind::Aggregate: { + const auto *Value = Initializer.getIf<GlobalAggregateInitializer>(); + return Value != nullptr && std::ranges::all_of(Value->Elements, [Depth](const auto &Element) { + return Element && initializerIsZero(*Element, Depth + 1); + }); + } + case GlobalInitializer::Kind::AlignmentPadding: { + const auto *Value = Initializer.getIf<GlobalAlignmentPadding>(); + return Value != nullptr && Value->Fill == 0; + } + case GlobalInitializer::Kind::Target: { + const auto *Value = Initializer.getIf<TargetConstant>(); + if (Value == nullptr) + return false; + auto Emission = Value->emit(); + return Emission && Emission->Fixups.empty() && + std::ranges::all_of(Emission->Bytes, + [](std::byte Byte) { return Byte == std::byte{0}; }); + } + case GlobalInitializer::Kind::Invalid: + case GlobalInitializer::Kind::SymbolAddress: + case GlobalInitializer::Kind::SymbolDifference: + return false; + } + return false; +} + +[[nodiscard]] bool initializerHasRelocation(const GlobalInitializer &Initializer, + std::size_t Depth = 0) { + if (Depth > 1024) + return true; + switch (Initializer.kind()) { + case GlobalInitializer::Kind::SymbolAddress: + case GlobalInitializer::Kind::SymbolDifference: + return true; + case GlobalInitializer::Kind::Repeated: { + const auto *Value = Initializer.getIf<GlobalRepeatedInitializer>(); + return Value == nullptr || !Value->Value || initializerHasRelocation(*Value->Value, Depth + 1); + } + case GlobalInitializer::Kind::Aggregate: { + const auto *Value = Initializer.getIf<GlobalAggregateInitializer>(); + return Value == nullptr || std::ranges::any_of(Value->Elements, [Depth](const auto &Element) { + return !Element || initializerHasRelocation(*Element, Depth + 1); + }); + } + case GlobalInitializer::Kind::Target: { + const auto *Value = Initializer.getIf<TargetConstant>(); + if (Value == nullptr) + return true; + auto Emission = Value->emit(); + return !Emission || !Emission->Fixups.empty(); + } + case GlobalInitializer::Kind::Invalid: + return true; + case GlobalInitializer::Kind::Integer: + case GlobalInitializer::Kind::Floating: + case GlobalInitializer::Kind::Bytes: + case GlobalInitializer::Kind::ZeroFill: + case GlobalInitializer::Kind::AlignmentPadding: + case GlobalInitializer::Kind::UndefinedStorage: + return false; + } + return true; +} + +[[nodiscard]] mc::MCSectionDesc textSectionDescription() { + return mc::MCSectionDesc{ + .Name = ".text", + .Kind = mc::MCSectionKind::Text, + .Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Executable}, + .Alignment = 4, + .EntrySize = 0, + .Group = {}, + .UniqueID = std::nullopt, + }; +} + +[[nodiscard]] mc::MCSectionDesc readOnlySectionDescription() { + return mc::MCSectionDesc{ + .Name = ".rodata", + .Kind = mc::MCSectionKind::ReadOnly, + .Flags = {mc::MCSectionFlag::Alloc}, + .Alignment = 1, + .EntrySize = 0, + .Group = {}, + .UniqueID = std::nullopt, + }; +} + +} // namespace + +Expected<mc::MCSectionDesc> RISCVELFTargetObjectFile::textSection(const MachineFunction &) const { + return textSectionDescription(); +} + +Expected<mc::MCSectionDesc> +RISCVELFTargetObjectFile::globalSection(const GlobalObjectDesc &Object, + MachineMCContext &Context) const { + if (Object.AddressSpace != 0) { + return makeUnexpected(ErrorCode::Unsupported, + "RV64GC assembly does not support nonzero global address spaces"); + } + if (Object.TargetFlags != 0 || !Object.TargetAttributes.empty()) { + return makeUnexpected(ErrorCode::Unsupported, + "RV64GC assembly does not recognize this global target state"); + } + const bool IsTLS = Object.TLSModel != GlobalTLSModel::None; + const bool IsZero = Object.Initializer && initializerIsZero(*Object.Initializer); + const bool HasRelocation = Object.Initializer && initializerHasRelocation(*Object.Initializer); + + mc::MCSectionDesc Result; + Result.Alignment = Object.Align.bytes(); + if (IsTLS) { + Result.Name = IsZero ? ".tbss" : ".tdata"; + Result.Kind = IsZero ? mc::MCSectionKind::TLSBSS : mc::MCSectionKind::TLSData; + Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable, mc::MCSectionFlag::TLS}; + } else if (Object.IsConstant && HasRelocation) { + Result.Name = ".data.rel.ro"; + Result.Kind = mc::MCSectionKind::Data; + Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable}; + } else if (Object.IsConstant) { + Result = readOnlySectionDescription(); + Result.Alignment = Object.Align.bytes(); + } else if (IsZero) { + Result.Name = ".bss"; + Result.Kind = mc::MCSectionKind::BSS; + Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable}; + } else { + Result.Name = ".data"; + Result.Kind = mc::MCSectionKind::Data; + Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable}; + } + + if (Object.Section) + Result.Name = *Object.Section; + if (Object.COMDAT) { + auto Group = Context.symbol(*Object.COMDAT); + if (!Group) + return std::unexpected(Group.error()); + Result.Flags.set(mc::MCSectionFlag::Group); + Result.Group = std::move(*Group); + if (!Object.Section) + Result.Name += "." + Object.Symbol; + } + return Result; +} + +Expected<mc::MCSectionDesc> +RISCVELFTargetObjectFile::constantPoolSection(const MachineFunction &Function, + const MachineConstantPoolEntry &Entry) const { + const MachineConstantPoolEntryDesc *Description = Entry.description(); + if (Description == nullptr) + return makeUnexpected(ErrorCode::InvalidArgument, "constant-pool entry is not live"); + if (Description->Placement.Section == ConstantPoolSectionKind::TextAdjacent) + return textSectionDescription(); + + mc::MCSectionDesc Result = readOnlySectionDescription(); + if (Description->Placement.Section == ConstantPoolSectionKind::Data) { + Result.Name = ".data"; + Result.Kind = mc::MCSectionKind::Data; + Result.Flags.set(mc::MCSectionFlag::Writable); + } else if (Description->Placement.Section == ConstantPoolSectionKind::Target) { + return makeUnexpected(ErrorCode::Unsupported, + "RV64GC constant pool has an unknown target section"); + } else if (Description->Placement.Section == ConstantPoolSectionKind::MergeableReadOnly) { + Result.Name = ".rodata.cst" + std::to_string(Description->Size); + Result.Flags.set(mc::MCSectionFlag::Merge); + Result.EntrySize = Description->Size; + } + if (Description->Placement.UniqueSection) { + Result.Name += "." + std::to_string(Function.stableID().value()) + "." + + std::to_string(Entry.index().value()); + Result.UniqueID = static_cast<std::uint64_t>(Entry.index().value()) + 1U; + } + Result.Alignment = Description->Align.bytes(); + return Result; +} + +Expected<mc::MCSectionDesc> +RISCVELFTargetObjectFile::jumpTableSection(const MachineFunction &Function, + const MachineJumpTableEntry &Entry) const { + const MachineJumpTableDesc *Description = Entry.description(); + if (Description == nullptr) + return makeUnexpected(ErrorCode::InvalidArgument, "jump-table entry is not live"); + mc::MCSectionDesc Result = Description->Placement.InTextSection ? textSectionDescription() + : readOnlySectionDescription(); + if (Description->Placement.UniqueSection) { + Result.Name += "." + std::to_string(Function.stableID().value()) + ".jt." + + std::to_string(Entry.index().value()); + Result.UniqueID = static_cast<std::uint64_t>(Entry.index().value()) + 1U; + } + Result.Alignment = Description->Align.bytes(); + return Result; +} + +} // namespace rcc::riscv diff --git a/backend/lib/Target/RISCV/RISCVTargetObjectFile.h b/backend/lib/Target/RISCV/RISCVTargetObjectFile.h new file mode 100644 index 0000000..cf8db36 --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVTargetObjectFile.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVTARGETOBJECTFILE_H +#define RCC_TARGET_RISCV_RISCVTARGETOBJECTFILE_H + +#include "rcc/CodeGen/TargetLoweringObjectFile.h" + +namespace rcc::riscv { + +class RISCVELFTargetObjectFile final : public TargetLoweringObjectFile { +public: + [[nodiscard]] Expected<mc::MCSectionDesc> + textSection(const MachineFunction &Function) const override; + [[nodiscard]] Expected<mc::MCSectionDesc> globalSection(const GlobalObjectDesc &Object, + MachineMCContext &Context) const override; + [[nodiscard]] Expected<mc::MCSectionDesc> + constantPoolSection(const MachineFunction &Function, + const MachineConstantPoolEntry &Entry) const override; + [[nodiscard]] Expected<mc::MCSectionDesc> + jumpTableSection(const MachineFunction &Function, + const MachineJumpTableEntry &Entry) const override; +}; + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVTARGETOBJECTFILE_H |
