aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-28 14:25:42 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-28 14:25:42 +0800
commit6889b82a5c9ecc6bd178760c66653d7af0121162 (patch)
treed0465db788912487c36bd676656f416eaa8b1642
parent6e8feb15a0d256ca19e88185f78c81f689ce7d34 (diff)
downloadrcc-project-6889b82a5c9ecc6bd178760c66653d7af0121162.tar.gz
rcc-project-6889b82a5c9ecc6bd178760c66653d7af0121162.tar.xz
[CodeGen] Separate assembly emission ownership
-rw-r--r--backend/include/rcc/CodeGen/AsmPrinter.h45
-rw-r--r--backend/include/rcc/CodeGen/TargetLoweringObjectFile.h34
-rw-r--r--backend/include/rcc/CodeGen/TargetMCInstLowering.h41
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVMCTargetLowering.h31
-rw-r--r--backend/lib/AsmPrinter/AsmPrinter.cpp437
-rw-r--r--backend/lib/AsmPrinter/CMakeLists.txt1
-rw-r--r--backend/lib/AsmPrinter/TargetMCInstLowering.cpp35
-rw-r--r--backend/lib/Target/RISCV/CMakeLists.txt6
-rw-r--r--backend/lib/Target/RISCV/RISCVAsmPrinter.cpp190
-rw-r--r--backend/lib/Target/RISCV/RISCVAsmPrinter.h28
-rw-r--r--backend/lib/Target/RISCV/RISCVAssemblyPrinter.cpp29
-rw-r--r--backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp788
-rw-r--r--backend/lib/Target/RISCV/RISCVObjectFile.cpp28
-rw-r--r--backend/lib/Target/RISCV/RISCVTargetObjectFile.cpp244
-rw-r--r--backend/lib/Target/RISCV/RISCVTargetObjectFile.h26
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