aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/MC
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib/MC')
-rw-r--r--backend/lib/MC/CMakeLists.txt73
-rw-r--r--backend/lib/MC/ELFObjectWriter.cpp704
-rw-r--r--backend/lib/MC/MCAsmBackend.cpp25
-rw-r--r--backend/lib/MC/MCAssembler.cpp846
-rw-r--r--backend/lib/MC/MCCFI.cpp70
-rw-r--r--backend/lib/MC/MCContext.cpp95
-rw-r--r--backend/lib/MC/MCDwarfFrameEmitter.cpp443
-rw-r--r--backend/lib/MC/MCExpr.cpp205
-rw-r--r--backend/lib/MC/MCFixup.cpp177
-rw-r--r--backend/lib/MC/MCInstPrinter.cpp170
-rw-r--r--backend/lib/MC/MCObjectStreamer.cpp132
-rw-r--r--backend/lib/MC/MCSection.cpp57
-rw-r--r--backend/lib/MC/MCStreamer.cpp501
-rw-r--r--backend/lib/MC/TargetRegistry.cpp73
14 files changed, 3571 insertions, 0 deletions
diff --git a/backend/lib/MC/CMakeLists.txt b/backend/lib/MC/CMakeLists.txt
new file mode 100644
index 0000000..0a5dd43
--- /dev/null
+++ b/backend/lib/MC/CMakeLists.txt
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+set(
+ RCCBackendMCSources
+ ELFObjectWriter.cpp
+ MCCFI.cpp
+ MCDwarfFrameEmitter.cpp
+ MCAsmBackend.cpp
+ MCAssembler.cpp
+ MCContext.cpp
+ MCExpr.cpp
+ MCFixup.cpp
+ MCInstPrinter.cpp
+ MCObjectStreamer.cpp
+ MCSection.cpp
+ MCStreamer.cpp
+ TargetRegistry.cpp
+)
+set(
+ RCCBackendMCHeaders
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/ELFObjectWriter.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCCFI.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCDwarfFrameEmitter.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCAsmBackend.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCAssembler.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCCodeEmitter.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCContext.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCExpr.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCFixup.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCInst.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCInstPrinter.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCObjectStreamer.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCObjectWriter.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCSection.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCStreamer.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCSymbol.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/TargetRegistry.h"
+)
+
+add_library(RCCBackendMC ${RCCBackendMCSources})
+add_library(RCCBackend::MC ALIAS RCCBackendMC)
+
+rcc_backend_configure_target(RCCBackendMC)
+
+target_include_directories(
+ RCCBackendMC
+ PUBLIC
+ "$<BUILD_INTERFACE:${RCC_BACKEND_SOURCE_DIR}/include>"
+ "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
+)
+target_link_libraries(RCCBackendMC PUBLIC RCCBackend::Support RCCBackend::Target)
+
+set_target_properties(
+ RCCBackendMC
+ PROPERTIES
+ EXPORT_NAME MC
+ OUTPUT_NAME RCCBackendMC
+)
+
+install(
+ TARGETS RCCBackendMC
+ EXPORT RCCBackendTargets
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+)
+
+set(RCCBackendMCSourcePaths)
+foreach(Source IN LISTS RCCBackendMCSources)
+ list(APPEND RCCBackendMCSourcePaths "${CMAKE_CURRENT_SOURCE_DIR}/${Source}")
+endforeach()
+
+rcc_backend_register_format_files(${RCCBackendMCSourcePaths} ${RCCBackendMCHeaders})
diff --git a/backend/lib/MC/ELFObjectWriter.cpp b/backend/lib/MC/ELFObjectWriter.cpp
new file mode 100644
index 0000000..96ec234
--- /dev/null
+++ b/backend/lib/MC/ELFObjectWriter.cpp
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/ELFObjectWriter.h"
+
+#include <algorithm>
+#include <bit>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <optional>
+#include <span>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+namespace rcc::mc {
+namespace {
+
+constexpr std::uint32_t SHTNull = 0;
+constexpr std::uint32_t SHTProgBits = 1;
+constexpr std::uint32_t SHTSymTab = 2;
+constexpr std::uint32_t SHTStrTab = 3;
+constexpr std::uint32_t SHTRela = 4;
+constexpr std::uint32_t SHTNoBits = 8;
+constexpr std::uint32_t SHTRel = 9;
+constexpr std::uint32_t SHTGroup = 17;
+constexpr std::uint32_t SHTSymTabShndx = 18;
+constexpr std::uint16_t SHNUndefined = 0;
+constexpr std::uint32_t SHNLoReserve = 0xff00U;
+constexpr std::uint16_t SHNCommon = 0xfff2U;
+constexpr std::uint32_t GRPComdat = 1;
+
+constexpr std::uint64_t SHFWrite = 0x1;
+constexpr std::uint64_t SHFAlloc = 0x2;
+constexpr std::uint64_t SHFExecInstr = 0x4;
+constexpr std::uint64_t SHFMerge = 0x10;
+constexpr std::uint64_t SHFStrings = 0x20;
+constexpr std::uint64_t SHFInfoLink = 0x40;
+constexpr std::uint64_t SHFGroup = 0x200;
+constexpr std::uint64_t SHFTLS = 0x400;
+constexpr std::uint64_t SHFGNURetain = 0x200000;
+
+class StringTable final {
+public:
+ StringTable() {
+ Bytes.push_back(0);
+ }
+
+ [[nodiscard]] Expected<std::uint32_t> add(std::string_view Value) {
+ const auto Existing = Offsets.find(std::string(Value));
+ if (Existing != Offsets.end())
+ return Existing->second;
+ constexpr std::size_t Maximum = std::numeric_limits<std::uint32_t>::max();
+ if (Value.size() > Maximum - 1U || Bytes.size() > Maximum - Value.size() - 1U) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF string table exceeds 4 GiB");
+ }
+ const auto Offset = static_cast<std::uint32_t>(Bytes.size());
+ Bytes.insert(Bytes.end(), Value.begin(), Value.end());
+ Bytes.push_back(0);
+ Offsets.emplace(std::string(Value), Offset);
+ return Offset;
+ }
+
+ [[nodiscard]] const std::vector<std::uint8_t> &bytes() const noexcept {
+ return Bytes;
+ }
+
+private:
+ std::vector<std::uint8_t> Bytes;
+ std::unordered_map<std::string, std::uint32_t> Offsets;
+};
+
+struct OutputSymbol final {
+ std::uint32_t Name = 0;
+ std::uint8_t Info = 0;
+ std::uint8_t Other = 0;
+ std::uint16_t Section = SHNUndefined;
+ std::uint64_t Value = 0;
+ std::uint64_t Size = 0;
+ const MCSymbol *Identity = nullptr;
+};
+
+struct OutputSection final {
+ std::string Name;
+ std::uint32_t NameOffset = 0;
+ std::uint32_t Type = SHTProgBits;
+ std::uint64_t Flags = 0;
+ std::uint64_t Alignment = 1;
+ std::uint64_t EntrySize = 0;
+ std::uint32_t Link = 0;
+ std::uint32_t Info = 0;
+ std::vector<std::uint8_t> Contents;
+ std::uint64_t AddressSize = 0;
+ std::uint64_t FileOffset = 0;
+ const MCSection *Input = nullptr;
+ std::optional<std::size_t> RelocationTarget;
+ MCSymbolRef GroupSignature;
+};
+
+[[nodiscard]] bool isWriterManagedSectionType(std::uint32_t Type) noexcept {
+ switch (Type) {
+ case SHTNull:
+ case SHTSymTab:
+ case SHTStrTab:
+ case SHTRela:
+ case SHTNoBits:
+ case SHTRel:
+ case SHTGroup:
+ case SHTSymTabShndx:
+ return true;
+ default:
+ return false;
+ }
+}
+
+[[nodiscard]] Expected<std::uint64_t> checkedAdd(std::uint64_t Left, std::uint64_t Right,
+ std::string_view Message) {
+ std::uint64_t Result = 0;
+ if (__builtin_add_overflow(Left, Right, &Result))
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, std::string(Message));
+ return Result;
+}
+
+[[nodiscard]] Expected<std::uint64_t> alignTo(std::uint64_t Value, std::uint64_t Alignment) {
+ if (!std::has_single_bit(Alignment))
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF alignment is not a power of two");
+ auto Adjusted = checkedAdd(Value, Alignment - 1U, "ELF file offset overflows");
+ if (!Adjusted)
+ return std::unexpected(Adjusted.error());
+ return *Adjusted & ~(Alignment - 1U);
+}
+
+void appendU16(std::vector<std::uint8_t> &Output, std::uint16_t Value) {
+ Output.push_back(static_cast<std::uint8_t>(Value));
+ Output.push_back(static_cast<std::uint8_t>(Value >> 8U));
+}
+
+void appendU32(std::vector<std::uint8_t> &Output, std::uint32_t Value) {
+ for (std::uint32_t Shift = 0; Shift < 32; Shift += 8)
+ Output.push_back(static_cast<std::uint8_t>(Value >> Shift));
+}
+
+void appendU64(std::vector<std::uint8_t> &Output, std::uint64_t Value) {
+ for (std::uint32_t Shift = 0; Shift < 64; Shift += 8)
+ Output.push_back(static_cast<std::uint8_t>(Value >> Shift));
+}
+
+void writeU64(std::vector<std::uint8_t> &Output, std::size_t Offset, std::uint64_t Value) {
+ for (std::uint32_t Shift = 0; Shift < 64; Shift += 8)
+ Output[Offset + Shift / 8U] = static_cast<std::uint8_t>(Value >> Shift);
+}
+
+[[nodiscard]] std::uint64_t sectionFlags(MCSectionFlags Flags) noexcept {
+ std::uint64_t Result = 0;
+ if (Flags.has(MCSectionFlag::Writable))
+ Result |= SHFWrite;
+ if (Flags.has(MCSectionFlag::Alloc))
+ Result |= SHFAlloc;
+ if (Flags.has(MCSectionFlag::Executable))
+ Result |= SHFExecInstr;
+ if (Flags.has(MCSectionFlag::Merge))
+ Result |= SHFMerge;
+ if (Flags.has(MCSectionFlag::Strings))
+ Result |= SHFStrings;
+ if (Flags.has(MCSectionFlag::Group))
+ Result |= SHFGroup;
+ if (Flags.has(MCSectionFlag::TLS))
+ Result |= SHFTLS;
+ if (Flags.has(MCSectionFlag::Retain))
+ Result |= SHFGNURetain;
+ return Result;
+}
+
+[[nodiscard]] std::uint8_t symbolBinding(MCObjectSymbolBinding Binding) noexcept {
+ switch (Binding) {
+ case MCObjectSymbolBinding::Local:
+ return 0;
+ case MCObjectSymbolBinding::Global:
+ return 1;
+ case MCObjectSymbolBinding::Weak:
+ return 2;
+ }
+ return 0;
+}
+
+[[nodiscard]] std::uint8_t symbolType(MCObjectSymbolType Type) noexcept {
+ switch (Type) {
+ case MCObjectSymbolType::None:
+ return 0;
+ case MCObjectSymbolType::Object:
+ return 1;
+ case MCObjectSymbolType::Function:
+ return 2;
+ case MCObjectSymbolType::Section:
+ return 3;
+ case MCObjectSymbolType::TLSObject:
+ return 6;
+ }
+ return 0;
+}
+
+[[nodiscard]] std::uint8_t symbolVisibility(MCObjectSymbolVisibility Visibility) noexcept {
+ switch (Visibility) {
+ case MCObjectSymbolVisibility::Default:
+ return 0;
+ case MCObjectSymbolVisibility::Hidden:
+ return 2;
+ case MCObjectSymbolVisibility::Protected:
+ return 3;
+ }
+ return 0;
+}
+
+[[nodiscard]] Expected<void> appendAtOffset(std::vector<std::uint8_t> &Output, std::uint64_t Offset,
+ std::span<const std::uint8_t> Contents) {
+ if (Offset > std::numeric_limits<std::size_t>::max() ||
+ Contents.size() >
+ std::numeric_limits<std::size_t>::max() - static_cast<std::size_t>(Offset)) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF output exceeds the host limit");
+ }
+ const std::size_t Position = static_cast<std::size_t>(Offset);
+ if (Output.size() > Position) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF output sections overlap in file layout");
+ }
+ if (Output.size() < Position)
+ Output.resize(Position);
+ Output.insert(Output.end(), Contents.begin(), Contents.end());
+ return {};
+}
+
+} // namespace
+
+Expected<std::vector<std::uint8_t>>
+ELF64LEObjectWriter::writeObject(const MCAssembledObject &Object) const {
+ if (Target.Machine == 0)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF target machine is not configured");
+
+ constexpr std::size_t ReservedOutputSections = 4;
+ constexpr std::size_t MaximumInputAndExtraSections = SHNLoReserve - ReservedOutputSections - 1U;
+ if (Object.Sections.size() > MaximumInputAndExtraSections ||
+ Target.ExtraSections.size() > MaximumInputAndExtraSections - Object.Sections.size()) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "ELF extended section numbering is not implemented");
+ }
+
+ std::vector<OutputSection> Sections;
+ Sections.push_back(OutputSection{.Name = {},
+ .NameOffset = 0,
+ .Type = SHTNull,
+ .Flags = 0,
+ .Alignment = 0,
+ .EntrySize = 0,
+ .Link = 0,
+ .Info = 0,
+ .Contents = {},
+ .AddressSize = 0,
+ .FileOffset = 0,
+ .Input = nullptr,
+ .RelocationTarget = std::nullopt,
+ .GroupSignature = {}});
+ const auto RequireSectionCapacity = [&Sections](std::size_t Additional) -> Expected<void> {
+ if (Sections.size() >= SHNLoReserve || Additional >= SHNLoReserve - Sections.size()) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "ELF extended section numbering is not implemented");
+ }
+ return {};
+ };
+ auto InputCapacity = RequireSectionCapacity(Object.Sections.size());
+ if (!InputCapacity)
+ return std::unexpected(InputCapacity.error());
+ std::vector<std::uint32_t> InputSectionIndices(Object.Sections.size());
+ std::unordered_map<const MCSection *, bool> ExistingSections;
+ for (std::size_t Index = 0; Index < Object.Sections.size(); ++Index) {
+ const MCObjectSection &Input = Object.Sections[Index];
+ if (!Input.Section)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF input section is null");
+ if (!ExistingSections.emplace(Input.Section.get(), true).second) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF input contains a duplicate section identity");
+ }
+ const MCSectionDesc &Description = Input.Section->description();
+ auto DescriptionVerification = Description.verify();
+ if (!DescriptionVerification)
+ return std::unexpected(DescriptionVerification.error());
+ const bool NoBits =
+ Description.Kind == MCSectionKind::BSS || Description.Kind == MCSectionKind::TLSBSS;
+ if (!std::has_single_bit(Input.Alignment) || Input.Alignment < Description.Alignment) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF input section has an invalid effective alignment");
+ }
+ if ((!NoBits && Input.AddressSize != Input.Contents.size()) ||
+ (NoBits && !Input.Contents.empty())) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF input section contents disagree with its address size");
+ }
+ if (NoBits && !Input.Relocations.empty()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF NOBITS section cannot contain relocations");
+ }
+ const auto OutputIndex = static_cast<std::uint32_t>(Sections.size());
+ InputSectionIndices[Index] = OutputIndex;
+ Sections.push_back(
+ OutputSection{.Name = Description.Name,
+ .NameOffset = 0,
+ .Type = NoBits ? SHTNoBits : SHTProgBits,
+ .Flags = sectionFlags(Description.Flags),
+ .Alignment = Input.Alignment,
+ .EntrySize = Description.EntrySize,
+ .Link = 0,
+ .Info = 0,
+ .Contents = NoBits ? std::vector<std::uint8_t>{} : Input.Contents,
+ .AddressSize = Input.AddressSize,
+ .FileOffset = 0,
+ .Input = Input.Section.get(),
+ .RelocationTarget = std::nullopt,
+ .GroupSignature = Description.Group});
+ }
+
+ for (const ELFExtraSection &Extra : Target.ExtraSections) {
+ if (Extra.Name.empty() || isWriterManagedSectionType(Extra.Type) ||
+ !std::has_single_bit(Extra.Alignment) || (Extra.Flags & (SHFInfoLink | SHFGroup)) != 0 ||
+ (Extra.EntrySize != 0 && Extra.Contents.size() % Extra.EntrySize != 0)) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF extra section has an invalid description");
+ }
+ auto ExtraCapacity = RequireSectionCapacity(1);
+ if (!ExtraCapacity)
+ return std::unexpected(ExtraCapacity.error());
+ Sections.push_back(OutputSection{.Name = Extra.Name,
+ .NameOffset = 0,
+ .Type = Extra.Type,
+ .Flags = Extra.Flags,
+ .Alignment = Extra.Alignment,
+ .EntrySize = Extra.EntrySize,
+ .Link = 0,
+ .Info = 0,
+ .Contents = Extra.Contents,
+ .AddressSize = Extra.Contents.size(),
+ .FileOffset = 0,
+ .Input = nullptr,
+ .RelocationTarget = std::nullopt,
+ .GroupSignature = {}});
+ }
+
+ StringTable SymbolNames;
+ std::vector<OutputSymbol> LocalSymbols;
+ std::vector<OutputSymbol> GlobalSymbols;
+ std::unordered_map<const MCSymbol *, bool> ExistingSymbols;
+ for (const MCObjectSymbol &Input : Object.Symbols) {
+ if (!Input.Symbol)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF input symbol is null");
+ if (!ExistingSymbols.emplace(Input.Symbol.get(), true).second) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF input contains a duplicate symbol identity");
+ }
+ auto Name = SymbolNames.add(Input.Symbol->name());
+ if (!Name)
+ return std::unexpected(Name.error());
+ std::uint16_t Section = SHNUndefined;
+ std::uint64_t Value = Input.Value;
+ if (Input.CommonAlignment != 0) {
+ if (!std::has_single_bit(Input.CommonAlignment) || Input.Size == 0 || Input.SectionIndex ||
+ Input.Binding == MCObjectSymbolBinding::Local || Input.Value != Input.CommonAlignment) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF common symbol has an invalid definition");
+ }
+ Section = SHNCommon;
+ Value = Input.CommonAlignment;
+ } else if (Input.SectionIndex) {
+ if (*Input.SectionIndex >= InputSectionIndices.size()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF symbol references an invalid section");
+ }
+ const std::uint32_t Mapped = InputSectionIndices[*Input.SectionIndex];
+ if (Mapped >= SHNLoReserve) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "ELF extended section indices are not implemented");
+ }
+ Section = static_cast<std::uint16_t>(Mapped);
+ const std::uint64_t SectionSize = Object.Sections[*Input.SectionIndex].AddressSize;
+ if (Input.Value > SectionSize || Input.Size > SectionSize - Input.Value) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF symbol range exceeds its section");
+ }
+ } else if (Input.Value != 0) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF undefined symbol has a nonzero value");
+ }
+ OutputSymbol Symbol{.Name = *Name,
+ .Info = static_cast<std::uint8_t>((symbolBinding(Input.Binding) << 4U) |
+ symbolType(Input.Type)),
+ .Other = symbolVisibility(Input.Visibility),
+ .Section = Section,
+ .Value = Value,
+ .Size = Input.Size,
+ .Identity = Input.Symbol.get()};
+ if (Input.Binding == MCObjectSymbolBinding::Local)
+ LocalSymbols.push_back(Symbol);
+ else
+ GlobalSymbols.push_back(Symbol);
+ }
+
+ for (const OutputSection &Section : Sections) {
+ if (!Section.GroupSignature || ExistingSymbols.contains(Section.GroupSignature.get()))
+ continue;
+ auto Name = SymbolNames.add(Section.GroupSignature->name());
+ if (!Name)
+ return std::unexpected(Name.error());
+ GlobalSymbols.push_back(OutputSymbol{.Name = *Name,
+ .Info = static_cast<std::uint8_t>(1U << 4U),
+ .Other = 0,
+ .Section = SHNUndefined,
+ .Value = 0,
+ .Size = 0,
+ .Identity = Section.GroupSignature.get()});
+ ExistingSymbols.emplace(Section.GroupSignature.get(), true);
+ }
+
+ std::vector<OutputSymbol> Symbols;
+ Symbols.push_back(OutputSymbol{});
+ for (std::size_t Index = 1; Index < Sections.size(); ++Index) {
+ Symbols.push_back(OutputSymbol{.Name = 0,
+ .Info = static_cast<std::uint8_t>(3),
+ .Other = 0,
+ .Section = static_cast<std::uint16_t>(Index),
+ .Value = 0,
+ .Size = 0,
+ .Identity = nullptr});
+ }
+ constexpr std::size_t MaximumSymbolCount = std::numeric_limits<std::uint32_t>::max();
+ if (LocalSymbols.size() > MaximumSymbolCount - Symbols.size()) {
+ return makeUnexpected(ErrorCode::Unsupported, "ELF symbol index exceeds 32 bits");
+ }
+ Symbols.insert(Symbols.end(), LocalSymbols.begin(), LocalSymbols.end());
+ const std::uint32_t FirstGlobal = static_cast<std::uint32_t>(Symbols.size());
+ if (GlobalSymbols.size() > MaximumSymbolCount - Symbols.size()) {
+ return makeUnexpected(ErrorCode::Unsupported, "ELF symbol index exceeds 32 bits");
+ }
+ Symbols.insert(Symbols.end(), GlobalSymbols.begin(), GlobalSymbols.end());
+ std::unordered_map<const MCSymbol *, std::uint32_t> SymbolIndices;
+ for (std::size_t Index = 0; Index < Symbols.size(); ++Index) {
+ if (Symbols[Index].Identity)
+ SymbolIndices.emplace(Symbols[Index].Identity, static_cast<std::uint32_t>(Index));
+ }
+
+ for (std::size_t InputIndex = 0; InputIndex < Object.Sections.size(); ++InputIndex) {
+ const MCObjectSection &Input = Object.Sections[InputIndex];
+ if (Input.Relocations.empty())
+ continue;
+ if (Input.Relocations.size() > std::numeric_limits<std::size_t>::max() / 24U) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF relocation section exceeds the host limit");
+ }
+ std::vector<std::uint8_t> Contents;
+ Contents.reserve(Input.Relocations.size() * 24U);
+ for (const MCObjectRelocation &Relocation : Input.Relocations) {
+ const auto Symbol = SymbolIndices.find(Relocation.Symbol.get());
+ if (!Relocation.Symbol || Symbol == SymbolIndices.end()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF relocation references an unknown symbol");
+ }
+ if (Relocation.Type == 0 || Relocation.Offset >= Input.AddressSize) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF relocation has an invalid type or section offset");
+ }
+ appendU64(Contents, Relocation.Offset);
+ appendU64(Contents, (static_cast<std::uint64_t>(Symbol->second) << 32U) | Relocation.Type);
+ appendU64(Contents, static_cast<std::uint64_t>(Relocation.Addend));
+ }
+ const std::uint32_t TargetSection = InputSectionIndices[InputIndex];
+ const MCSymbolRef GroupSignature = Sections[TargetSection].GroupSignature;
+ auto RelocationCapacity = RequireSectionCapacity(1);
+ if (!RelocationCapacity)
+ return std::unexpected(RelocationCapacity.error());
+ Sections.push_back(OutputSection{.Name = ".rela" + std::string(Input.Section->name()),
+ .NameOffset = 0,
+ .Type = SHTRela,
+ .Flags = SHFInfoLink | (GroupSignature ? SHFGroup : 0),
+ .Alignment = 8,
+ .EntrySize = 24,
+ .Link = 0,
+ .Info = TargetSection,
+ .Contents = std::move(Contents),
+ .AddressSize = 0,
+ .FileOffset = 0,
+ .Input = nullptr,
+ .RelocationTarget = InputIndex,
+ .GroupSignature = GroupSignature});
+ }
+
+ struct PendingGroup final {
+ MCSymbolRef Signature;
+ std::vector<std::uint32_t> Members;
+ };
+ std::vector<PendingGroup> Groups;
+ for (std::size_t SectionIndex = 1; SectionIndex < Sections.size(); ++SectionIndex) {
+ if (!Sections[SectionIndex].GroupSignature)
+ continue;
+ const auto Existing = std::ranges::find_if(Groups, [&](const PendingGroup &Group) {
+ return Group.Signature == Sections[SectionIndex].GroupSignature;
+ });
+ if (Existing == Groups.end()) {
+ Groups.push_back(PendingGroup{
+ .Signature = Sections[SectionIndex].GroupSignature,
+ .Members = {static_cast<std::uint32_t>(SectionIndex)},
+ });
+ } else {
+ Existing->Members.push_back(static_cast<std::uint32_t>(SectionIndex));
+ }
+ }
+ for (const PendingGroup &Group : Groups) {
+ const auto Signature = SymbolIndices.find(Group.Signature.get());
+ if (Signature == SymbolIndices.end() || (Symbols[Signature->second].Info >> 4U) == 0) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF COMDAT group requires a nonlocal signature symbol");
+ }
+ std::vector<std::uint8_t> Contents;
+ appendU32(Contents, GRPComdat);
+ for (const std::uint32_t Member : Group.Members)
+ appendU32(Contents, Member);
+ auto GroupCapacity = RequireSectionCapacity(1);
+ if (!GroupCapacity)
+ return std::unexpected(GroupCapacity.error());
+ Sections.push_back(OutputSection{.Name = ".group",
+ .NameOffset = 0,
+ .Type = SHTGroup,
+ .Flags = 0,
+ .Alignment = 4,
+ .EntrySize = 4,
+ .Link = 0,
+ .Info = Signature->second,
+ .Contents = std::move(Contents),
+ .AddressSize = 0,
+ .FileOffset = 0,
+ .Input = nullptr,
+ .RelocationTarget = std::nullopt,
+ .GroupSignature = {}});
+ }
+
+ auto MetadataCapacity = RequireSectionCapacity(3);
+ if (!MetadataCapacity)
+ return std::unexpected(MetadataCapacity.error());
+ const std::uint32_t SymTabIndex = static_cast<std::uint32_t>(Sections.size());
+ const std::uint32_t StrTabIndex = SymTabIndex + 1U;
+ for (OutputSection &Section : Sections) {
+ if (Section.Type == SHTGroup || Section.Type == SHTRela)
+ Section.Link = SymTabIndex;
+ }
+
+ std::vector<std::uint8_t> SymbolContents;
+ if (Symbols.size() > std::numeric_limits<std::size_t>::max() / 24U) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF symbol table exceeds the host limit");
+ }
+ SymbolContents.reserve(Symbols.size() * 24U);
+ for (const OutputSymbol &Symbol : Symbols) {
+ appendU32(SymbolContents, Symbol.Name);
+ SymbolContents.push_back(Symbol.Info);
+ SymbolContents.push_back(Symbol.Other);
+ appendU16(SymbolContents, Symbol.Section);
+ appendU64(SymbolContents, Symbol.Value);
+ appendU64(SymbolContents, Symbol.Size);
+ }
+ Sections.push_back(OutputSection{.Name = ".symtab",
+ .NameOffset = 0,
+ .Type = SHTSymTab,
+ .Flags = 0,
+ .Alignment = 8,
+ .EntrySize = 24,
+ .Link = StrTabIndex,
+ .Info = FirstGlobal,
+ .Contents = std::move(SymbolContents),
+ .AddressSize = 0,
+ .FileOffset = 0,
+ .Input = nullptr,
+ .RelocationTarget = std::nullopt,
+ .GroupSignature = {}});
+ Sections.push_back(OutputSection{.Name = ".strtab",
+ .NameOffset = 0,
+ .Type = SHTStrTab,
+ .Flags = 0,
+ .Alignment = 1,
+ .EntrySize = 0,
+ .Link = 0,
+ .Info = 0,
+ .Contents = SymbolNames.bytes(),
+ .AddressSize = 0,
+ .FileOffset = 0,
+ .Input = nullptr,
+ .RelocationTarget = std::nullopt,
+ .GroupSignature = {}});
+
+ StringTable SectionNames;
+ for (OutputSection &Section : Sections) {
+ auto Name = SectionNames.add(Section.Name);
+ if (!Name)
+ return std::unexpected(Name.error());
+ Section.NameOffset = *Name;
+ }
+ const std::uint32_t ShStrTabIndex = static_cast<std::uint32_t>(Sections.size());
+ auto ShStrName = SectionNames.add(".shstrtab");
+ if (!ShStrName)
+ return std::unexpected(ShStrName.error());
+ Sections.push_back(OutputSection{.Name = ".shstrtab",
+ .NameOffset = *ShStrName,
+ .Type = SHTStrTab,
+ .Flags = 0,
+ .Alignment = 1,
+ .EntrySize = 0,
+ .Link = 0,
+ .Info = 0,
+ .Contents = SectionNames.bytes(),
+ .AddressSize = 0,
+ .FileOffset = 0,
+ .Input = nullptr,
+ .RelocationTarget = std::nullopt,
+ .GroupSignature = {}});
+
+ if (Sections.size() >= SHNLoReserve) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "ELF extended section numbering is not implemented");
+ }
+
+ std::uint64_t FileOffset = 64;
+ for (std::size_t Index = 1; Index < Sections.size(); ++Index) {
+ OutputSection &Section = Sections[Index];
+ auto Aligned = alignTo(FileOffset, Section.Alignment);
+ if (!Aligned)
+ return std::unexpected(Aligned.error());
+ Section.FileOffset = *Aligned;
+ if (Section.Type == SHTNoBits)
+ continue;
+ auto End = checkedAdd(*Aligned, Section.Contents.size(), "ELF section file range overflows");
+ if (!End)
+ return std::unexpected(End.error());
+ FileOffset = *End;
+ }
+ auto SectionHeaderOffset = alignTo(FileOffset, 8);
+ if (!SectionHeaderOffset)
+ return std::unexpected(SectionHeaderOffset.error());
+ if (Sections.size() > std::numeric_limits<std::uint64_t>::max() / 64U) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "ELF section header table exceeds the host limit");
+ }
+ auto End = checkedAdd(*SectionHeaderOffset, static_cast<std::uint64_t>(Sections.size()) * 64U,
+ "ELF section header table overflows");
+ if (!End || *End > std::numeric_limits<std::size_t>::max()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "ELF output exceeds the host limit");
+ }
+
+ std::vector<std::uint8_t> Output(64, 0);
+ Output[0] = 0x7f;
+ Output[1] = 'E';
+ Output[2] = 'L';
+ Output[3] = 'F';
+ Output[4] = 2;
+ Output[5] = 1;
+ Output[6] = 1;
+ Output[7] = Target.OSABI;
+ Output[8] = Target.ABIVersion;
+ Output[16] = 1;
+ Output[18] = static_cast<std::uint8_t>(Target.Machine);
+ Output[19] = static_cast<std::uint8_t>(Target.Machine >> 8U);
+ Output[20] = 1;
+ writeU64(Output, 40, *SectionHeaderOffset);
+ Output[48] = static_cast<std::uint8_t>(Target.Flags | Object.Flags);
+ Output[49] = static_cast<std::uint8_t>((Target.Flags | Object.Flags) >> 8U);
+ Output[50] = static_cast<std::uint8_t>((Target.Flags | Object.Flags) >> 16U);
+ Output[51] = static_cast<std::uint8_t>((Target.Flags | Object.Flags) >> 24U);
+ Output[52] = 64;
+ Output[58] = 64;
+ Output[60] = static_cast<std::uint8_t>(Sections.size());
+ Output[61] = static_cast<std::uint8_t>(Sections.size() >> 8U);
+ Output[62] = static_cast<std::uint8_t>(ShStrTabIndex);
+ Output[63] = static_cast<std::uint8_t>(ShStrTabIndex >> 8U);
+
+ for (std::size_t Index = 1; Index < Sections.size(); ++Index) {
+ if (Sections[Index].Type == SHTNoBits)
+ continue;
+ auto Appended = appendAtOffset(Output, Sections[Index].FileOffset, Sections[Index].Contents);
+ if (!Appended)
+ return std::unexpected(Appended.error());
+ }
+ if (Output.size() < *SectionHeaderOffset)
+ Output.resize(static_cast<std::size_t>(*SectionHeaderOffset));
+ for (const OutputSection &Section : Sections) {
+ appendU32(Output, Section.NameOffset);
+ appendU32(Output, Section.Type);
+ appendU64(Output, Section.Flags);
+ appendU64(Output, 0);
+ appendU64(Output, Section.FileOffset);
+ appendU64(Output, Section.Type == SHTNoBits ? Section.AddressSize : Section.Contents.size());
+ appendU32(Output, Section.Link);
+ appendU32(Output, Section.Info);
+ appendU64(Output, Section.Alignment);
+ appendU64(Output, Section.EntrySize);
+ }
+ return Output;
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCAsmBackend.cpp b/backend/lib/MC/MCAsmBackend.cpp
new file mode 100644
index 0000000..bc350b7
--- /dev/null
+++ b/backend/lib/MC/MCAsmBackend.cpp
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCAsmBackend.h"
+
+namespace rcc::mc {
+
+Expected<std::vector<MCFixupRelocation>>
+MCAsmBackend::relocationsForFixup(const MCFixup &Fixup, const MCValue &Value) const {
+ if (!Value.AddSymbol || Value.SubtractSymbol) {
+ return makeUnexpected(ErrorCode::RelocationFailed,
+ "MC backend cannot represent this expression as one relocation");
+ }
+ auto Type = relocationType(Fixup, Value);
+ if (!Type)
+ return std::unexpected(Type.error());
+ return std::vector<MCFixupRelocation>{
+ {.Type = *Type, .Symbol = Value.AddSymbol, .Addend = Value.Addend}};
+}
+
+Expected<MCInst> MCAsmBackend::relaxInstruction(const MCInst &) const {
+ return makeUnexpected(ErrorCode::InstructionEncodingFailed,
+ "target does not support MC instruction relaxation");
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCAssembler.cpp b/backend/lib/MC/MCAssembler.cpp
new file mode 100644
index 0000000..e1eb8e0
--- /dev/null
+++ b/backend/lib/MC/MCAssembler.cpp
@@ -0,0 +1,846 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCAssembler.h"
+
+#include <algorithm>
+#include <bit>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <optional>
+#include <utility>
+
+namespace rcc::mc {
+namespace {
+
+[[nodiscard]] Expected<std::uint64_t> checkedAdd(std::uint64_t Left, std::uint64_t Right,
+ std::string_view Message) {
+ std::uint64_t Result = 0;
+ if (__builtin_add_overflow(Left, Right, &Result))
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, std::string(Message));
+ return Result;
+}
+
+[[nodiscard]] Expected<std::int64_t> checkedAdd(std::int64_t Left, std::int64_t Right,
+ std::string_view Message) {
+ std::int64_t Result = 0;
+ if (__builtin_add_overflow(Left, Right, &Result))
+ return makeUnexpected(ErrorCode::RelocationFailed, std::string(Message));
+ return Result;
+}
+
+[[nodiscard]] Expected<std::int64_t> checkedSubtract(std::int64_t Left, std::int64_t Right,
+ std::string_view Message) {
+ std::int64_t Result = 0;
+ if (__builtin_sub_overflow(Left, Right, &Result))
+ return makeUnexpected(ErrorCode::RelocationFailed, std::string(Message));
+ return Result;
+}
+
+[[nodiscard]] Expected<std::int64_t> signedOffset(std::uint64_t Value, std::string_view Message) {
+ if (Value > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max()))
+ return makeUnexpected(ErrorCode::RelocationFailed, std::string(Message));
+ return static_cast<std::int64_t>(Value);
+}
+
+[[nodiscard]] Expected<std::uint64_t> alignmentPadding(std::uint64_t Offset,
+ std::uint64_t Alignment) {
+ if (!std::has_single_bit(Alignment))
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC alignment is not a power of two");
+ const std::uint64_t Mask = Alignment - 1U;
+ auto Adjusted = checkedAdd(Offset, Mask, "MC section alignment overflows");
+ if (!Adjusted)
+ return std::unexpected(Adjusted.error());
+ return (*Adjusted & ~Mask) - Offset;
+}
+
+[[nodiscard]] MCFixupKind dataFixupKind(std::uint8_t Size) noexcept {
+ switch (Size) {
+ case 1:
+ return fixup_kind::Data1;
+ case 2:
+ return fixup_kind::Data2;
+ case 4:
+ return fixup_kind::Data4;
+ case 8:
+ return fixup_kind::Data8;
+ default:
+ return fixup_kind::None;
+ }
+}
+
+void appendLittleEndian(std::vector<std::uint8_t> &Bytes, std::uint64_t Value, std::uint8_t Size) {
+ for (std::uint8_t Index = 0; Index < Size; ++Index)
+ Bytes.push_back(static_cast<std::uint8_t>(Value >> (Index * 8U)));
+}
+
+[[nodiscard]] bool isNoBits(const MCSection &Section) noexcept {
+ return Section.description().Kind == MCSectionKind::BSS ||
+ Section.description().Kind == MCSectionKind::TLSBSS;
+}
+
+} // namespace
+
+Expected<std::size_t> MCAssembler::currentSectionIndex() const {
+ if (WasAssembled)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "MC assembler is already finalized");
+ if (!CurrentSection)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "object emission requires an active MC section");
+ return *CurrentSection;
+}
+
+MCAssembler::SymbolData &MCAssembler::getOrCreateSymbol(MCSymbolRef Symbol) {
+ const auto Position = SymbolIndices.find(Symbol.get());
+ if (Position != SymbolIndices.end())
+ return Symbols[Position->second];
+ const std::size_t Index = Symbols.size();
+ Symbols.push_back(SymbolData{.Symbol = std::move(Symbol),
+ .Binding = MCObjectSymbolBinding::Local,
+ .BindingWasSet = false,
+ .Type = MCObjectSymbolType::None,
+ .Visibility = MCObjectSymbolVisibility::Default,
+ .SectionIndex = std::nullopt,
+ .FragmentIndex = 0,
+ .Size = {},
+ .CommonSize = 0,
+ .CommonAlignment = 0});
+ SymbolIndices.emplace(Symbols.back().Symbol.get(), Index);
+ return Symbols.back();
+}
+
+std::optional<std::uint64_t> MCAssembler::symbolOffset(const SymbolData &Symbol) const {
+ if (!Symbol.SectionIndex || *Symbol.SectionIndex >= Sections.size())
+ return std::nullopt;
+ const SectionData &Section = Sections[*Symbol.SectionIndex];
+ if (Symbol.FragmentIndex > Section.Fragments.size())
+ return std::nullopt;
+ if (Symbol.FragmentIndex == Section.Fragments.size())
+ return Section.AddressSize;
+ return Section.Fragments[Symbol.FragmentIndex].Offset;
+}
+
+Expected<void> MCAssembler::switchSection(MCSectionRef Section) {
+ if (WasAssembled)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "MC assembler is already finalized");
+ if (!Section)
+ return makeUnexpected(ErrorCode::InvalidArgument, "cannot switch to a null MC section");
+ auto Verification = Section->description().verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ const auto Existing = SectionIndices.find(Section.get());
+ if (Existing != SectionIndices.end()) {
+ CurrentSection = Existing->second;
+ return {};
+ }
+ const std::size_t Index = Sections.size();
+ const std::uint64_t Alignment = Section->description().Alignment;
+ Sections.push_back(SectionData{
+ .Section = std::move(Section), .Fragments = {}, .AddressSize = 0, .Alignment = Alignment});
+ SectionIndices.emplace(Sections.back().Section.get(), Index);
+ CurrentSection = Index;
+ return {};
+}
+
+Expected<void> MCAssembler::emitLabel(MCSymbolRef Symbol) {
+ auto SectionIndex = currentSectionIndex();
+ if (!SectionIndex)
+ return std::unexpected(SectionIndex.error());
+ if (!Symbol)
+ return makeUnexpected(ErrorCode::InvalidArgument, "cannot emit a null MC symbol label");
+ SymbolData &Data = getOrCreateSymbol(std::move(Symbol));
+ if (Data.SectionIndex || Data.CommonSize != 0) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "MC symbol is defined more than once");
+ }
+ Data.SectionIndex = *SectionIndex;
+ Data.FragmentIndex = Sections[*SectionIndex].Fragments.size();
+ return {};
+}
+
+Expected<void> MCAssembler::emitInstruction(const MCInst &Instruction) {
+ auto SectionIndex = currentSectionIndex();
+ if (!SectionIndex)
+ return std::unexpected(SectionIndex.error());
+ if (!Instruction.opcode().isValid())
+ return makeUnexpected(ErrorCode::InvalidArgument, "cannot encode an invalid MC opcode");
+ Sections[*SectionIndex].Fragments.push_back(
+ Fragment{.Payload = InstructionFragment{.Instruction = Instruction},
+ .Offset = 0,
+ .Size = 0,
+ .EncodedBytes = {},
+ .Fixups = {}});
+ return {};
+}
+
+Expected<void> MCAssembler::emitValue(MCExprRef Value, std::uint8_t Size) {
+ auto SectionIndex = currentSectionIndex();
+ if (!SectionIndex)
+ return std::unexpected(SectionIndex.error());
+ const MCFixupKind Kind = dataFixupKind(Size);
+ if (!Value || Kind == fixup_kind::None) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "MC value requires an expression and a supported size");
+ }
+ DataFragment Data;
+ if (const auto Absolute = Value->evaluateAbsolute()) {
+ appendLittleEndian(Data.Bytes, static_cast<std::uint64_t>(*Absolute), Size);
+ } else {
+ Data.Bytes.resize(Size);
+ Data.Fixups.push_back(MCFixup{.Offset = 0, .Value = std::move(Value), .Kind = Kind});
+ }
+ Sections[*SectionIndex].Fragments.push_back(Fragment{
+ .Payload = std::move(Data), .Offset = 0, .Size = 0, .EncodedBytes = {}, .Fixups = {}});
+ return {};
+}
+
+Expected<void> MCAssembler::emitBytes(std::span<const std::uint8_t> Bytes) {
+ auto SectionIndex = currentSectionIndex();
+ if (!SectionIndex)
+ return std::unexpected(SectionIndex.error());
+ if (Bytes.empty())
+ return {};
+ Sections[*SectionIndex].Fragments.push_back(
+ Fragment{.Payload = DataFragment{.Bytes = {Bytes.begin(), Bytes.end()}, .Fixups = {}},
+ .Offset = 0,
+ .Size = 0,
+ .EncodedBytes = {},
+ .Fixups = {}});
+ return {};
+}
+
+Expected<void> MCAssembler::emitZeroFill(std::uint64_t Size) {
+ auto SectionIndex = currentSectionIndex();
+ if (!SectionIndex)
+ return std::unexpected(SectionIndex.error());
+ if (Size == 0)
+ return {};
+ Sections[*SectionIndex].Fragments.push_back(Fragment{.Payload = ZeroFillFragment{.Size = Size},
+ .Offset = 0,
+ .Size = 0,
+ .EncodedBytes = {},
+ .Fixups = {}});
+ return {};
+}
+
+Expected<void> MCAssembler::emitAlignment(std::uint64_t Alignment, std::uint8_t Fill,
+ std::uint64_t MaximumBytes) {
+ auto SectionIndex = currentSectionIndex();
+ if (!SectionIndex)
+ return std::unexpected(SectionIndex.error());
+ if (!std::has_single_bit(Alignment))
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC alignment is not a power of two");
+ Sections[*SectionIndex].Fragments.push_back(Fragment{
+ .Payload =
+ AlignmentFragment{.Alignment = Alignment, .Fill = Fill, .MaximumBytes = MaximumBytes},
+ .Offset = 0,
+ .Size = 0,
+ .EncodedBytes = {},
+ .Fixups = {}});
+ return {};
+}
+
+Expected<void> MCAssembler::emitSymbolAttribute(MCSymbolRef Symbol, MCSymbolAttribute Attribute) {
+ if (WasAssembled)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "MC assembler is already finalized");
+ if (!Symbol)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC symbol attribute has no symbol");
+ SymbolData &Data = getOrCreateSymbol(std::move(Symbol));
+ switch (Attribute) {
+ case MCSymbolAttribute::Global:
+ Data.Binding = MCObjectSymbolBinding::Global;
+ Data.BindingWasSet = true;
+ break;
+ case MCSymbolAttribute::Weak:
+ Data.Binding = MCObjectSymbolBinding::Weak;
+ Data.BindingWasSet = true;
+ break;
+ case MCSymbolAttribute::Local:
+ Data.Binding = MCObjectSymbolBinding::Local;
+ Data.BindingWasSet = true;
+ break;
+ case MCSymbolAttribute::Hidden:
+ Data.Visibility = MCObjectSymbolVisibility::Hidden;
+ break;
+ case MCSymbolAttribute::Protected:
+ Data.Visibility = MCObjectSymbolVisibility::Protected;
+ break;
+ case MCSymbolAttribute::Function:
+ Data.Type = MCObjectSymbolType::Function;
+ break;
+ case MCSymbolAttribute::Object:
+ Data.Type = MCObjectSymbolType::Object;
+ break;
+ case MCSymbolAttribute::TLSObject:
+ Data.Type = MCObjectSymbolType::TLSObject;
+ break;
+ }
+ return {};
+}
+
+Expected<void> MCAssembler::emitSymbolSize(MCSymbolRef Symbol, MCExprRef Size) {
+ if (WasAssembled)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "MC assembler is already finalized");
+ if (!Symbol || !Size)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC symbol size is incomplete");
+ SymbolData &Data = getOrCreateSymbol(std::move(Symbol));
+ if (Data.Size)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "MC symbol size is defined twice");
+ Data.Size = std::move(Size);
+ return {};
+}
+
+Expected<void> MCAssembler::emitCommonSymbol(MCSymbolRef Symbol, std::uint64_t Size,
+ std::uint64_t Alignment) {
+ if (WasAssembled)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "MC assembler is already finalized");
+ if (!Symbol || Size == 0 || !std::has_single_bit(Alignment)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "common symbol requires a size and power-of-two alignment");
+ }
+ SymbolData &Data = getOrCreateSymbol(std::move(Symbol));
+ if (Data.SectionIndex || Data.CommonSize != 0)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "common symbol is defined twice");
+ Data.CommonSize = Size;
+ Data.CommonAlignment = Alignment;
+ if (Data.Binding == MCObjectSymbolBinding::Local)
+ Data.Binding = MCObjectSymbolBinding::Global;
+ if (Data.Type == MCObjectSymbolType::None)
+ Data.Type = MCObjectSymbolType::Object;
+ return {};
+}
+
+Expected<void> MCAssembler::configureDwarfFrame(MCSectionRef Section, MCDwarfFrameInfo Info) {
+ if (WasAssembled || !CFIProcedures.empty() || DwarfFrameInfo) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "DWARF frame emission is already configured or active");
+ }
+ if (!Section || Section->description().Kind != MCSectionKind::EHFrame) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "DWARF frame emission requires an EHFrame section");
+ }
+ auto SectionVerification = Section->description().verify();
+ if (!SectionVerification)
+ return std::unexpected(SectionVerification.error());
+ auto InfoVerification = Info.verify();
+ if (!InfoVerification)
+ return std::unexpected(InfoVerification.error());
+ EHFrameSection = std::move(Section);
+ DwarfFrameInfo = Info;
+ return {};
+}
+
+Expected<void> MCAssembler::emitCFI(const MCCFIInstruction &Instruction) {
+ if (WasAssembled)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "MC assembler is already finalized");
+ auto Verification = Instruction.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ auto SectionIndex = currentSectionIndex();
+ if (!SectionIndex)
+ return std::unexpected(SectionIndex.error());
+ const std::size_t FragmentIndex = Sections[*SectionIndex].Fragments.size();
+
+ if (Instruction.Operation == MCCFIOperation::StartProc) {
+ if (ActiveCFIProcedure) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "nested CFI procedures are not permitted");
+ }
+ MCSymbolRef Function;
+ for (auto Position = Symbols.rbegin(); Position != Symbols.rend(); ++Position) {
+ if (Position->SectionIndex == SectionIndex && Position->FragmentIndex == FragmentIndex &&
+ Position->Symbol && Position->Symbol->kind() == MCSymbolKind::Function) {
+ Function = Position->Symbol;
+ break;
+ }
+ }
+ if (!Function) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "CFI procedure does not begin at a function symbol");
+ }
+ CFIProcedures.push_back(CFIProcedure{.Function = std::move(Function),
+ .SectionIndex = *SectionIndex,
+ .BeginFragmentIndex = FragmentIndex,
+ .EndFragmentIndex = 0,
+ .Events = {},
+ .IsClosed = false});
+ ActiveCFIProcedure = CFIProcedures.size() - 1U;
+ return {};
+ }
+
+ if (!ActiveCFIProcedure) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "CFI event appears outside a procedure");
+ }
+ CFIProcedure &Procedure = CFIProcedures[*ActiveCFIProcedure];
+ if (Procedure.SectionIndex != *SectionIndex) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "CFI procedure changes sections before it ends");
+ }
+ if (Instruction.Operation == MCCFIOperation::EndProc) {
+ Procedure.EndFragmentIndex = FragmentIndex;
+ Procedure.IsClosed = true;
+ ActiveCFIProcedure.reset();
+ return {};
+ }
+ if (Instruction.Operation == MCCFIOperation::Label) {
+ return emitLabel(Instruction.Symbol);
+ }
+ Procedure.Events.push_back(CFIEvent{.Instruction = Instruction, .FragmentIndex = FragmentIndex});
+ return {};
+}
+
+Expected<void> MCAssembler::encodeFragments() {
+ for (SectionData &Section : Sections) {
+ for (Fragment &Fragment : Section.Fragments) {
+ Fragment.EncodedBytes.clear();
+ Fragment.Fixups.clear();
+ Fragment.Size = 0;
+ if (auto *Instruction = std::get_if<InstructionFragment>(&Fragment.Payload)) {
+ auto Encoded = Emitter->encodeInstruction(Instruction->Instruction);
+ if (!Encoded)
+ return std::unexpected(Encoded.error());
+ Fragment.EncodedBytes = std::move(Encoded->Bytes);
+ Fragment.Fixups = std::move(Encoded->Fixups);
+ } else if (auto *Data = std::get_if<DataFragment>(&Fragment.Payload)) {
+ Fragment.EncodedBytes = Data->Bytes;
+ Fragment.Fixups = Data->Fixups;
+ }
+ Fragment.Size = Fragment.EncodedBytes.size();
+ for (const MCFixup &Fixup : Fragment.Fixups) {
+ auto Verification = Fixup.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ auto KindInfo = Backend->fixupKindInfo(Fixup.Kind);
+ if (!KindInfo)
+ return std::unexpected(KindInfo.error());
+ if (Fixup.PCRelative != KindInfo->PCRelative) {
+ return makeUnexpected(ErrorCode::InstructionEncodingFailed,
+ "MC fixup PC-relative state disagrees with its kind");
+ }
+ if (Fixup.Offset > Fragment.EncodedBytes.size() ||
+ KindInfo->StorageSize >
+ Fragment.EncodedBytes.size() - static_cast<std::size_t>(Fixup.Offset)) {
+ return makeUnexpected(ErrorCode::InstructionEncodingFailed,
+ "MC fixup exceeds its encoded fragment");
+ }
+ }
+ }
+ }
+ return {};
+}
+
+Expected<bool> MCAssembler::layoutAndRelax() {
+ std::size_t RelaxableInstructionCount = 0;
+ for (const SectionData &Section : Sections) {
+ for (const Fragment &Fragment : Section.Fragments) {
+ const auto *Instruction = std::get_if<InstructionFragment>(&Fragment.Payload);
+ if (Instruction != nullptr && Backend->mayNeedRelaxation(Instruction->Instruction)) {
+ if (RelaxableInstructionCount == std::numeric_limits<std::size_t>::max()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "MC relaxation budget exceeds the host limit");
+ }
+ ++RelaxableInstructionCount;
+ }
+ }
+ }
+ const std::size_t MaximumIterations = RelaxableInstructionCount + 1U;
+ for (std::size_t Iteration = 0; Iteration < MaximumIterations; ++Iteration) {
+ auto Encoded = encodeFragments();
+ if (!Encoded)
+ return std::unexpected(Encoded.error());
+
+ for (SectionData &Section : Sections) {
+ std::uint64_t Offset = 0;
+ Section.Alignment = Section.Section->description().Alignment;
+ const bool NoBits = isNoBits(*Section.Section);
+ for (Fragment &Fragment : Section.Fragments) {
+ Fragment.Offset = Offset;
+ std::uint64_t Size = Fragment.Size;
+ if (const auto *Alignment = std::get_if<AlignmentFragment>(&Fragment.Payload)) {
+ auto Padding = alignmentPadding(Offset, Alignment->Alignment);
+ if (!Padding)
+ return std::unexpected(Padding.error());
+ Size = Alignment->MaximumBytes != 0 && *Padding > Alignment->MaximumBytes ? 0 : *Padding;
+ Section.Alignment = std::max(Section.Alignment, Alignment->Alignment);
+ if (NoBits)
+ Fragment.EncodedBytes.clear();
+ else
+ Fragment.EncodedBytes.assign(static_cast<std::size_t>(Size), Alignment->Fill);
+ } else if (const auto *Zero = std::get_if<ZeroFillFragment>(&Fragment.Payload)) {
+ Size = Zero->Size;
+ if (!NoBits && Size > std::numeric_limits<std::size_t>::max()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "zero-fill fragment exceeds the host size limit");
+ }
+ if (NoBits)
+ Fragment.EncodedBytes.clear();
+ else
+ Fragment.EncodedBytes.assign(static_cast<std::size_t>(Size), 0);
+ } else if (NoBits) {
+ if (std::holds_alternative<InstructionFragment>(Fragment.Payload) ||
+ !Fragment.Fixups.empty() ||
+ std::ranges::any_of(Fragment.EncodedBytes,
+ [](std::uint8_t Byte) { return Byte != 0; })) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "MC NOBITS section contains initialized data");
+ }
+ Fragment.EncodedBytes.clear();
+ }
+ Fragment.Size = Size;
+ auto End = checkedAdd(Offset, Size, "MC section size overflows");
+ if (!End)
+ return std::unexpected(End.error());
+ Offset = *End;
+ }
+ Section.AddressSize = Offset;
+ }
+
+ bool Changed = false;
+ for (SectionData &Section : Sections) {
+ for (Fragment &Fragment : Section.Fragments) {
+ auto *Instruction = std::get_if<InstructionFragment>(&Fragment.Payload);
+ if (Instruction == nullptr || !Backend->mayNeedRelaxation(Instruction->Instruction))
+ continue;
+ for (const MCFixup &Fixup : Fragment.Fixups) {
+ auto Value = evaluateAsRelocatable(*Fixup.Value);
+ if (!Value)
+ return std::unexpected(Value.error());
+ if (!Value->AddSymbol || Value->SubtractSymbol || Value->TargetKind != 0)
+ continue;
+ const auto SymbolPosition = SymbolIndices.find(Value->AddSymbol.get());
+ if (SymbolPosition == SymbolIndices.end())
+ continue;
+ const SymbolData &Target = Symbols[SymbolPosition->second];
+ if (!Target.SectionIndex || *Target.SectionIndex != SectionIndices[Section.Section.get()])
+ continue;
+ const auto TargetOffset = symbolOffset(Target);
+ if (!TargetOffset)
+ continue;
+ auto PlaceOffset = checkedAdd(Fragment.Offset, Fixup.Offset, "MC fixup place overflows");
+ if (!PlaceOffset)
+ return std::unexpected(PlaceOffset.error());
+ auto Place = signedOffset(*PlaceOffset, "MC fixup place exceeds the signed range");
+ auto TargetPosition =
+ signedOffset(*TargetOffset, "MC relaxation target exceeds the signed range");
+ if (!Place)
+ return std::unexpected(Place.error());
+ if (!TargetPosition)
+ return std::unexpected(TargetPosition.error());
+ auto WithAddend =
+ checkedAdd(*TargetPosition, Value->Addend, "MC relaxation target addend overflows");
+ if (!WithAddend)
+ return std::unexpected(WithAddend.error());
+ auto Displacement =
+ checkedSubtract(*WithAddend, *Place, "MC relaxation displacement overflows");
+ if (!Displacement)
+ return std::unexpected(Displacement.error());
+ auto NeedsRelaxation = Backend->fixupNeedsRelaxation(Fixup, *Displacement);
+ if (!NeedsRelaxation)
+ return std::unexpected(NeedsRelaxation.error());
+ if (!*NeedsRelaxation)
+ continue;
+ auto Relaxed = Backend->relaxInstruction(Instruction->Instruction);
+ if (!Relaxed)
+ return std::unexpected(Relaxed.error());
+ Instruction->Instruction = std::move(*Relaxed);
+ Changed = true;
+ break;
+ }
+ }
+ }
+ if (!Changed)
+ return false;
+ }
+ return makeUnexpected(ErrorCode::InternalError, "MC relaxation did not reach a fixed point");
+}
+
+Expected<MCAssembledObject> MCAssembler::finalizeObject() {
+ for (SectionData &Section : Sections) {
+ for (Fragment &Fragment : Section.Fragments) {
+ for (const MCFixup &Fixup : Fragment.Fixups) {
+ auto Value = evaluateAsRelocatable(*Fixup.Value);
+ if (!Value)
+ return std::unexpected(Value.error());
+ if (Value->AddSymbol)
+ static_cast<void>(getOrCreateSymbol(Value->AddSymbol));
+ if (Value->SubtractSymbol)
+ static_cast<void>(getOrCreateSymbol(Value->SubtractSymbol));
+ }
+ }
+ }
+
+ MCAssembledObject Result;
+ Result.Flags = Backend->objectFlags();
+ Result.Sections.reserve(Sections.size());
+ for (const SectionData &Section : Sections) {
+ MCObjectSection Output{.Section = Section.Section,
+ .Contents = {},
+ .AddressSize = Section.AddressSize,
+ .Alignment = Section.Alignment,
+ .Relocations = {}};
+ if (!isNoBits(*Section.Section) &&
+ Section.AddressSize > std::numeric_limits<std::size_t>::max()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "MC section exceeds the host size limit");
+ }
+ if (!isNoBits(*Section.Section)) {
+ Output.Contents.reserve(static_cast<std::size_t>(Section.AddressSize));
+ for (const Fragment &Fragment : Section.Fragments)
+ Output.Contents.insert(Output.Contents.end(), Fragment.EncodedBytes.begin(),
+ Fragment.EncodedBytes.end());
+ }
+ Result.Sections.push_back(std::move(Output));
+ }
+
+ Result.Symbols.reserve(Symbols.size());
+ for (const SymbolData &Data : Symbols) {
+ if (!Data.SectionIndex && Data.CommonSize == 0 && Data.Symbol->isTemporary()) {
+ return makeUnexpected(ErrorCode::RelocationFailed,
+ "temporary MC symbol is referenced but not defined");
+ }
+ const MCObjectSymbolBinding Binding =
+ !Data.BindingWasSet && !Data.SectionIndex && Data.CommonSize == 0
+ ? MCObjectSymbolBinding::Global
+ : Data.Binding;
+ MCObjectSymbol Output{.Symbol = Data.Symbol,
+ .Binding = Binding,
+ .Type = Data.Type,
+ .Visibility = Data.Visibility,
+ .SectionIndex = Data.SectionIndex,
+ .CommonAlignment = Data.CommonAlignment};
+ if (Data.CommonSize != 0) {
+ Output.Size = Data.CommonSize;
+ Output.Value = Data.CommonAlignment;
+ } else if (const auto Offset = symbolOffset(Data)) {
+ Output.Value = *Offset;
+ }
+ if (Data.Size) {
+ auto SizeValue = evaluateAsRelocatable(*Data.Size);
+ if (!SizeValue)
+ return std::unexpected(SizeValue.error());
+ if (SizeValue->TargetKind != 0) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "symbol size cannot use a target expression");
+ }
+ if (SizeValue->AddSymbol && SizeValue->SubtractSymbol) {
+ const auto AddPosition = SymbolIndices.find(SizeValue->AddSymbol.get());
+ const auto SubtractPosition = SymbolIndices.find(SizeValue->SubtractSymbol.get());
+ if (AddPosition == SymbolIndices.end() || SubtractPosition == SymbolIndices.end()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "symbol size is unresolved");
+ }
+ const SymbolData &Add = Symbols[AddPosition->second];
+ const SymbolData &Subtract = Symbols[SubtractPosition->second];
+ if (!Add.SectionIndex || !Subtract.SectionIndex ||
+ Add.SectionIndex != Subtract.SectionIndex) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "symbol size difference crosses sections");
+ }
+ }
+ std::int64_t Resolved = SizeValue->Addend;
+ const auto Resolve = [&](const MCSymbolRef &Symbol) -> Expected<std::int64_t> {
+ const auto Position = SymbolIndices.find(Symbol.get());
+ if (Position == SymbolIndices.end())
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "symbol size is unresolved");
+ const auto Offset = symbolOffset(Symbols[Position->second]);
+ if (!Offset)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "symbol size is unresolved");
+ return signedOffset(*Offset, "symbol size offset exceeds the signed range");
+ };
+ if (SizeValue->AddSymbol) {
+ auto Offset = Resolve(SizeValue->AddSymbol);
+ if (!Offset)
+ return std::unexpected(Offset.error());
+ auto Updated = checkedAdd(Resolved, *Offset, "symbol size addition overflows");
+ if (!Updated)
+ return std::unexpected(Updated.error());
+ Resolved = *Updated;
+ }
+ if (SizeValue->SubtractSymbol) {
+ auto Offset = Resolve(SizeValue->SubtractSymbol);
+ if (!Offset)
+ return std::unexpected(Offset.error());
+ auto Updated = checkedSubtract(Resolved, *Offset, "symbol size subtraction overflows");
+ if (!Updated)
+ return std::unexpected(Updated.error());
+ Resolved = *Updated;
+ }
+ if (Resolved < 0)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "symbol size is negative");
+ Output.Size = static_cast<std::uint64_t>(Resolved);
+ }
+ if (Output.SectionIndex) {
+ const std::uint64_t SectionSize = Sections[*Output.SectionIndex].AddressSize;
+ if (Output.Value > SectionSize || Output.Size > SectionSize - Output.Value) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "symbol range exceeds its defining section");
+ }
+ }
+ Result.Symbols.push_back(std::move(Output));
+ }
+
+ for (std::size_t SectionIndex = 0; SectionIndex < Sections.size(); ++SectionIndex) {
+ const SectionData &Section = Sections[SectionIndex];
+ MCObjectSection &Output = Result.Sections[SectionIndex];
+ for (const Fragment &Fragment : Section.Fragments) {
+ for (const MCFixup &Fixup : Fragment.Fixups) {
+ auto Value = evaluateAsRelocatable(*Fixup.Value);
+ if (!Value)
+ return std::unexpected(Value.error());
+ auto PlaceValue =
+ checkedAdd(Fragment.Offset, Fixup.Offset, "MC relocation offset overflows");
+ if (!PlaceValue)
+ return std::unexpected(PlaceValue.error());
+ const std::uint64_t Place = *PlaceValue;
+ bool Resolved = Value->isAbsolute();
+ std::int64_t FixedValue = Value->Addend;
+ const auto FindSymbol = [&](const MCSymbolRef &Symbol) -> const SymbolData * {
+ const auto Position = SymbolIndices.find(Symbol.get());
+ return Position == SymbolIndices.end() ? nullptr : &Symbols[Position->second];
+ };
+ const SymbolData *Add = Value->AddSymbol ? FindSymbol(Value->AddSymbol) : nullptr;
+ const SymbolData *Subtract =
+ Value->SubtractSymbol ? FindSymbol(Value->SubtractSymbol) : nullptr;
+ if (Value->TargetKind == 0 && Add != nullptr && Subtract != nullptr && Add->SectionIndex &&
+ Subtract->SectionIndex && Add->SectionIndex == Subtract->SectionIndex) {
+ const auto AddOffset = symbolOffset(*Add);
+ const auto SubtractOffset = symbolOffset(*Subtract);
+ if (AddOffset && SubtractOffset) {
+ auto SignedAdd = signedOffset(*AddOffset, "MC symbol offset exceeds the signed range");
+ auto SignedSubtract =
+ signedOffset(*SubtractOffset, "MC symbol offset exceeds the signed range");
+ if (!SignedAdd)
+ return std::unexpected(SignedAdd.error());
+ if (!SignedSubtract)
+ return std::unexpected(SignedSubtract.error());
+ auto Difference =
+ checkedSubtract(*SignedAdd, *SignedSubtract, "MC symbol difference overflows");
+ if (!Difference)
+ return std::unexpected(Difference.error());
+ auto Updated = checkedAdd(FixedValue, *Difference, "MC resolved fixup value overflows");
+ if (!Updated)
+ return std::unexpected(Updated.error());
+ FixedValue = *Updated;
+ Resolved = true;
+ }
+ } else if (Value->TargetKind == 0 && Fixup.PCRelative && Add != nullptr &&
+ !Value->SubtractSymbol && Add->SectionIndex &&
+ *Add->SectionIndex == SectionIndex) {
+ const auto AddOffset = symbolOffset(*Add);
+ if (AddOffset) {
+ auto SignedAdd = signedOffset(*AddOffset, "MC symbol offset exceeds the signed range");
+ auto SignedPlace = signedOffset(Place, "MC fixup place exceeds the signed range");
+ if (!SignedAdd)
+ return std::unexpected(SignedAdd.error());
+ if (!SignedPlace)
+ return std::unexpected(SignedPlace.error());
+ auto Difference =
+ checkedSubtract(*SignedAdd, *SignedPlace, "MC PC-relative displacement overflows");
+ if (!Difference)
+ return std::unexpected(Difference.error());
+ auto Updated = checkedAdd(FixedValue, *Difference, "MC resolved fixup value overflows");
+ if (!Updated)
+ return std::unexpected(Updated.error());
+ FixedValue = *Updated;
+ Resolved = true;
+ }
+ }
+
+ if (Resolved) {
+ auto Applied = Backend->applyFixup(Fixup.Kind, Output.Contents, Place, FixedValue);
+ if (!Applied)
+ return std::unexpected(Applied.error());
+ continue;
+ }
+ auto Relocations = Backend->relocationsForFixup(Fixup, *Value);
+ if (!Relocations)
+ return std::unexpected(Relocations.error());
+ for (const MCFixupRelocation &Relocation : *Relocations) {
+ if (Relocation.Type == 0 || !Relocation.Symbol) {
+ return makeUnexpected(ErrorCode::RelocationFailed,
+ "MC backend produced an invalid relocation");
+ }
+ Output.Relocations.push_back(MCObjectRelocation{.Offset = Place,
+ .Type = Relocation.Type,
+ .Symbol = Relocation.Symbol,
+ .Addend = Relocation.Addend});
+ }
+ }
+ }
+ }
+ auto DwarfFrame = appendDwarfFrame(Result);
+ if (!DwarfFrame)
+ return std::unexpected(DwarfFrame.error());
+ return Result;
+}
+
+Expected<void> MCAssembler::appendDwarfFrame(MCAssembledObject &Object) const {
+ if (CFIProcedures.empty())
+ return {};
+ if (!DwarfFrameInfo || !EHFrameSection) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "CFI was emitted without DWARF frame configuration");
+ }
+ if (ActiveCFIProcedure) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "CFI procedure was not terminated");
+ }
+
+ std::vector<MCDwarfFrameProcedure> Procedures;
+ Procedures.reserve(CFIProcedures.size());
+ for (const CFIProcedure &Procedure : CFIProcedures) {
+ if (!Procedure.IsClosed || Procedure.SectionIndex >= Sections.size()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "CFI procedure is incomplete");
+ }
+ const SectionData &Section = Sections[Procedure.SectionIndex];
+ const auto FragmentOffset = [&Section](std::size_t Index) -> std::optional<std::uint64_t> {
+ if (Index > Section.Fragments.size())
+ return std::nullopt;
+ if (Index == Section.Fragments.size())
+ return Section.AddressSize;
+ return Section.Fragments[Index].Offset;
+ };
+ const auto Begin = FragmentOffset(Procedure.BeginFragmentIndex);
+ const auto End = FragmentOffset(Procedure.EndFragmentIndex);
+ if (!Begin || !End || *End < *Begin) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "CFI procedure has an invalid address range");
+ }
+ MCDwarfFrameProcedure Output{
+ .Function = Procedure.Function, .AddressRange = *End - *Begin, .Instructions = {}};
+ Output.Instructions.reserve(Procedure.Events.size());
+ for (const CFIEvent &Event : Procedure.Events) {
+ const auto Offset = FragmentOffset(Event.FragmentIndex);
+ if (!Offset || *Offset < *Begin || *Offset > *End) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "CFI event lies outside its procedure");
+ }
+ Output.Instructions.push_back(MCDwarfFrameInstruction{
+ .Instruction = Event.Instruction,
+ .Offset = *Offset - *Begin,
+ });
+ }
+ Procedures.push_back(std::move(Output));
+ }
+ auto Frame = emitDwarfEHFrame(EHFrameSection, Procedures, *DwarfFrameInfo, *Backend);
+ if (!Frame)
+ return std::unexpected(Frame.error());
+ Object.Sections.push_back(std::move(*Frame));
+ return {};
+}
+
+Expected<MCAssembledObject> MCAssembler::assemble() {
+ if (WasAssembled)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "MC assembler was already finalized");
+ if (ActiveCFIProcedure) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "CFI procedure was not terminated");
+ }
+ MCAssembler Working = *this;
+ auto Relaxed = Working.layoutAndRelax();
+ if (!Relaxed)
+ return std::unexpected(Relaxed.error());
+ auto Result = Working.finalizeObject();
+ if (!Result)
+ return std::unexpected(Result.error());
+ WasAssembled = true;
+ return Result;
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCCFI.cpp b/backend/lib/MC/MCCFI.cpp
new file mode 100644
index 0000000..aa66e0f
--- /dev/null
+++ b/backend/lib/MC/MCCFI.cpp
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCCFI.h"
+
+#include <cstddef>
+
+namespace rcc::mc {
+
+Expected<void> MCCFIInstruction::verify() const {
+ std::size_t ExpectedOperands = 0;
+ bool RequiresBytes = false;
+ bool RequiresSymbol = false;
+
+ switch (Operation) {
+ case MCCFIOperation::StartProc:
+ case MCCFIOperation::EndProc:
+ case MCCFIOperation::Sections:
+ case MCCFIOperation::SignalFrame:
+ case MCCFIOperation::RememberState:
+ case MCCFIOperation::RestoreState:
+ case MCCFIOperation::WindowSave:
+ case MCCFIOperation::NegateReturnAddressState:
+ case MCCFIOperation::NegateReturnAddressStateWithPC:
+ break;
+ case MCCFIOperation::Personality:
+ case MCCFIOperation::LSDA:
+ ExpectedOperands = 1;
+ RequiresSymbol = true;
+ break;
+ case MCCFIOperation::ReturnColumn:
+ case MCCFIOperation::SameValue:
+ case MCCFIOperation::DefCFARegister:
+ case MCCFIOperation::DefCFAOffset:
+ case MCCFIOperation::AdjustCFAOffset:
+ case MCCFIOperation::Restore:
+ case MCCFIOperation::Undefined:
+ case MCCFIOperation::GNUArgsSize:
+ ExpectedOperands = 1;
+ break;
+ case MCCFIOperation::Offset:
+ case MCCFIOperation::DefCFA:
+ case MCCFIOperation::RelativeOffset:
+ case MCCFIOperation::Register:
+ case MCCFIOperation::ValueOffset:
+ ExpectedOperands = 2;
+ break;
+ case MCCFIOperation::Escape:
+ RequiresBytes = true;
+ break;
+ case MCCFIOperation::Label:
+ RequiresSymbol = true;
+ break;
+ }
+
+ if (Operands.size() != ExpectedOperands)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC CFI event has an invalid operand count");
+ if (RequiresBytes != !Bytes.empty()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ RequiresBytes ? "MC CFI escape event requires bytes"
+ : "non-escape MC CFI event cannot contain bytes");
+ }
+ if (RequiresSymbol != static_cast<bool>(Symbol)) {
+ return makeUnexpected(ErrorCode::InvalidArgument, RequiresSymbol
+ ? "MC CFI event requires a symbol"
+ : "MC CFI event cannot contain a symbol");
+ }
+ return {};
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCContext.cpp b/backend/lib/MC/MCContext.cpp
new file mode 100644
index 0000000..2aa4c0e
--- /dev/null
+++ b/backend/lib/MC/MCContext.cpp
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCContext.h"
+
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+
+namespace rcc::mc {
+namespace {
+
+[[nodiscard]] bool isValidName(std::string_view Name) noexcept {
+ if (Name.empty())
+ return false;
+ for (const char RawCharacter : Name) {
+ const auto Character = static_cast<unsigned char>(RawCharacter);
+ if (Character == 0 || Character == '\n' || Character == '\r' || Character == 0x7fU)
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+Expected<MCSymbolRef> MCContext::getOrCreateSymbol(std::string Name, MCSymbolKind Kind) {
+ if (!isValidName(Name))
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC symbol has an invalid name");
+
+ const auto Position = Symbols.find(Name);
+ if (Position != Symbols.end()) {
+ const MCSymbolKind ExistingKind = Position->second->kind();
+ if (Kind != MCSymbolKind::Generic && ExistingKind != MCSymbolKind::Generic &&
+ ExistingKind != Kind) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "MC symbol name is already used by a different symbol kind");
+ }
+ return Position->second;
+ }
+
+ MCSymbolRef Result(new MCSymbol(std::move(Name), Kind));
+ Symbols.emplace(std::string(Result->name()), Result);
+ return Result;
+}
+
+Expected<MCSymbolRef> MCContext::createTemporarySymbol(std::string_view Prefix) {
+ if (!isValidName(Prefix))
+ return makeUnexpected(ErrorCode::InvalidArgument, "temporary MC symbol prefix is invalid");
+ if (NextTemporarySymbol == std::numeric_limits<std::uint64_t>::max()) {
+ return makeUnexpected(ErrorCode::InternalError, "temporary MC symbol sequence is exhausted");
+ }
+
+ while (true) {
+ std::string Name(Prefix);
+ Name += std::to_string(NextTemporarySymbol++);
+ if (Symbols.contains(Name))
+ continue;
+ return getOrCreateSymbol(std::move(Name), MCSymbolKind::Temporary);
+ }
+}
+
+MCSymbolRef MCContext::symbol(std::string_view Name) const noexcept {
+ const auto Position = Symbols.find(Name);
+ return Position == Symbols.end() ? MCSymbolRef{} : Position->second;
+}
+
+Expected<MCSectionRef> MCContext::getOrCreateSection(MCSectionDesc Description) {
+ auto Verification = Description.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+
+ const auto Position = Sections.find(Description.Name);
+ if (Position != Sections.end()) {
+ MCSectionDesc ExistingDescription = Position->second->description();
+ ExistingDescription.Alignment = Description.Alignment;
+ if (ExistingDescription != Description) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "MC section name is already used by a different description");
+ }
+ return Position->second;
+ }
+
+ MCSectionRef Result(new MCSection(std::move(Description)));
+ Sections.emplace(std::string(Result->name()), Result);
+ return Result;
+}
+
+MCSectionRef MCContext::section(std::string_view Name) const noexcept {
+ const auto Position = Sections.find(Name);
+ return Position == Sections.end() ? MCSectionRef{} : Position->second;
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCDwarfFrameEmitter.cpp b/backend/lib/MC/MCDwarfFrameEmitter.cpp
new file mode 100644
index 0000000..581c8cb
--- /dev/null
+++ b/backend/lib/MC/MCDwarfFrameEmitter.cpp
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCDwarfFrameEmitter.h"
+
+#include "rcc/MC/MCExpr.h"
+#include "rcc/MC/MCFixup.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace rcc::mc {
+namespace {
+
+constexpr std::uint8_t DWCFAAdvanceLoc = 0x40;
+constexpr std::uint8_t DWCFAOffset = 0x80;
+constexpr std::uint8_t DWCFARestore = 0xc0;
+constexpr std::uint8_t DWCFANop = 0x00;
+constexpr std::uint8_t DWCFAAdvanceLoc1 = 0x02;
+constexpr std::uint8_t DWCFAAdvanceLoc2 = 0x03;
+constexpr std::uint8_t DWCFAAdvanceLoc4 = 0x04;
+constexpr std::uint8_t DWCFAOffsetExtended = 0x05;
+constexpr std::uint8_t DWCFARestoreExtended = 0x06;
+constexpr std::uint8_t DWCFAUndefined = 0x07;
+constexpr std::uint8_t DWCFASameValue = 0x08;
+constexpr std::uint8_t DWCFARegister = 0x09;
+constexpr std::uint8_t DWCFARememberState = 0x0a;
+constexpr std::uint8_t DWCFARestoreState = 0x0b;
+constexpr std::uint8_t DWCFADefCFA = 0x0c;
+constexpr std::uint8_t DWCFADefCFARegister = 0x0d;
+constexpr std::uint8_t DWCFADefCFAOffset = 0x0e;
+constexpr std::uint8_t DWCFAValOffset = 0x14;
+constexpr std::uint8_t DWCFAGNUWindowSave = 0x2d;
+constexpr std::uint8_t DWCFAGNUArgsSize = 0x2e;
+constexpr std::uint8_t DWEHPEPCRelativeSData4 = 0x1b;
+
+void appendU16(std::vector<std::uint8_t> &Bytes, std::uint16_t Value) {
+ Bytes.push_back(static_cast<std::uint8_t>(Value));
+ Bytes.push_back(static_cast<std::uint8_t>(Value >> 8U));
+}
+
+void appendU32(std::vector<std::uint8_t> &Bytes, std::uint32_t Value) {
+ for (std::uint8_t Shift = 0; Shift < 32; Shift += 8)
+ Bytes.push_back(static_cast<std::uint8_t>(Value >> Shift));
+}
+
+void writeU32(std::vector<std::uint8_t> &Bytes, std::size_t Offset, std::uint32_t Value) {
+ for (std::uint8_t Shift = 0; Shift < 32; Shift += 8)
+ Bytes[Offset + Shift / 8U] = static_cast<std::uint8_t>(Value >> Shift);
+}
+
+void appendULEB128(std::vector<std::uint8_t> &Bytes, std::uint64_t Value) {
+ do {
+ std::uint8_t Byte = static_cast<std::uint8_t>(Value & 0x7fU);
+ Value >>= 7U;
+ if (Value != 0)
+ Byte |= 0x80U;
+ Bytes.push_back(Byte);
+ } while (Value != 0);
+}
+
+void appendSLEB128(std::vector<std::uint8_t> &Bytes, std::int64_t Value) {
+ bool More = true;
+ while (More) {
+ std::uint8_t Byte = static_cast<std::uint8_t>(Value & 0x7f);
+ const bool HasSign = (Byte & 0x40U) != 0;
+ Value >>= 7;
+ More = (Value != 0 || HasSign) && (Value != -1 || !HasSign);
+ if (More)
+ Byte |= 0x80U;
+ Bytes.push_back(Byte);
+ }
+}
+
+[[nodiscard]] Expected<void> appendAdvance(std::vector<std::uint8_t> &Bytes, std::uint64_t Delta,
+ const MCDwarfFrameInfo &Info) {
+ if (Delta == 0)
+ return {};
+ if (Delta % Info.CodeAlignment != 0) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "CFI location is not aligned to the DWARF code factor");
+ }
+ const std::uint64_t Factored = Delta / Info.CodeAlignment;
+ if (Factored <= 0x3fU) {
+ Bytes.push_back(static_cast<std::uint8_t>(DWCFAAdvanceLoc | Factored));
+ } else if (Factored <= std::numeric_limits<std::uint8_t>::max()) {
+ Bytes.push_back(DWCFAAdvanceLoc1);
+ Bytes.push_back(static_cast<std::uint8_t>(Factored));
+ } else if (Factored <= std::numeric_limits<std::uint16_t>::max()) {
+ Bytes.push_back(DWCFAAdvanceLoc2);
+ appendU16(Bytes, static_cast<std::uint16_t>(Factored));
+ } else if (Factored <= std::numeric_limits<std::uint32_t>::max()) {
+ Bytes.push_back(DWCFAAdvanceLoc4);
+ appendU32(Bytes, static_cast<std::uint32_t>(Factored));
+ } else {
+ return makeUnexpected(ErrorCode::Unsupported, "DWARF CFI location delta exceeds 32 bits");
+ }
+ return {};
+}
+
+[[nodiscard]] Expected<std::uint64_t> registerNumber(std::int64_t Value) {
+ if (Value < 0 || static_cast<std::uint64_t>(Value) > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "DWARF CFI register number is out of range");
+ }
+ return static_cast<std::uint64_t>(Value);
+}
+
+[[nodiscard]] Expected<std::uint64_t> factoredOffset(std::int64_t Offset,
+ std::int64_t DataAlignment) {
+ if (Offset == std::numeric_limits<std::int64_t>::min() && DataAlignment == -1) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "DWARF CFI offset factor overflows");
+ }
+ if (Offset % DataAlignment != 0) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "DWARF CFI offset is not divisible by its data factor");
+ }
+ const std::int64_t Factored = Offset / DataAlignment;
+ if (Factored < 0) {
+ return makeUnexpected(ErrorCode::Unsupported, "DWARF CFI rule requires a signed offset opcode");
+ }
+ return static_cast<std::uint64_t>(Factored);
+}
+
+void appendOffsetRule(std::vector<std::uint8_t> &Bytes, std::uint64_t Register,
+ std::uint64_t Offset) {
+ if (Register <= 0x3fU) {
+ Bytes.push_back(static_cast<std::uint8_t>(DWCFAOffset | Register));
+ } else {
+ Bytes.push_back(DWCFAOffsetExtended);
+ appendULEB128(Bytes, Register);
+ }
+ appendULEB128(Bytes, Offset);
+}
+
+void appendRestoreRule(std::vector<std::uint8_t> &Bytes, std::uint64_t Register) {
+ if (Register <= 0x3fU) {
+ Bytes.push_back(static_cast<std::uint8_t>(DWCFARestore | Register));
+ } else {
+ Bytes.push_back(DWCFARestoreExtended);
+ appendULEB128(Bytes, Register);
+ }
+}
+
+struct CFAState final {
+ std::uint64_t Register = 0;
+ std::int64_t Offset = 0;
+};
+
+[[nodiscard]] Expected<void>
+appendFrameInstructions(std::vector<std::uint8_t> &Bytes,
+ std::span<const MCDwarfFrameInstruction> Instructions,
+ const MCDwarfFrameInfo &Info) {
+ CFAState State{.Register = Info.InitialCFARegister,
+ .Offset = static_cast<std::int64_t>(Info.InitialCFAOffset)};
+ std::vector<CFAState> StateStack;
+ std::uint64_t PreviousOffset = 0;
+ for (const MCDwarfFrameInstruction &Entry : Instructions) {
+ auto Verification = Entry.Instruction.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ if (Entry.Offset < PreviousOffset) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "DWARF CFI locations are not monotonic");
+ }
+ if (Entry.Instruction.Operation == MCCFIOperation::Label)
+ continue;
+ auto Advance = appendAdvance(Bytes, Entry.Offset - PreviousOffset, Info);
+ if (!Advance)
+ return std::unexpected(Advance.error());
+ PreviousOffset = Entry.Offset;
+
+ const auto &Operands = Entry.Instruction.Operands;
+ switch (Entry.Instruction.Operation) {
+ case MCCFIOperation::SameValue: {
+ auto Register = registerNumber(Operands[0]);
+ if (!Register)
+ return std::unexpected(Register.error());
+ Bytes.push_back(DWCFASameValue);
+ appendULEB128(Bytes, *Register);
+ break;
+ }
+ case MCCFIOperation::RememberState:
+ Bytes.push_back(DWCFARememberState);
+ StateStack.push_back(State);
+ break;
+ case MCCFIOperation::RestoreState:
+ if (StateStack.empty()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "DWARF CFI restore_state has no saved state");
+ }
+ Bytes.push_back(DWCFARestoreState);
+ State = StateStack.back();
+ StateStack.pop_back();
+ break;
+ case MCCFIOperation::Offset:
+ case MCCFIOperation::RelativeOffset: {
+ auto Register = registerNumber(Operands[0]);
+ if (!Register)
+ return std::unexpected(Register.error());
+ std::int64_t Offset = Operands[1];
+ if (Entry.Instruction.Operation == MCCFIOperation::RelativeOffset &&
+ __builtin_sub_overflow(Offset, State.Offset, &Offset)) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "relative DWARF CFI offset overflows");
+ }
+ auto Factored = factoredOffset(Offset, Info.DataAlignment);
+ if (!Factored)
+ return std::unexpected(Factored.error());
+ appendOffsetRule(Bytes, *Register, *Factored);
+ break;
+ }
+ case MCCFIOperation::DefCFARegister: {
+ auto Register = registerNumber(Operands[0]);
+ if (!Register)
+ return std::unexpected(Register.error());
+ Bytes.push_back(DWCFADefCFARegister);
+ appendULEB128(Bytes, *Register);
+ State.Register = *Register;
+ break;
+ }
+ case MCCFIOperation::DefCFAOffset:
+ if (Operands[0] < 0) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "DWARF CFA offset cannot be negative");
+ }
+ Bytes.push_back(DWCFADefCFAOffset);
+ appendULEB128(Bytes, static_cast<std::uint64_t>(Operands[0]));
+ State.Offset = Operands[0];
+ break;
+ case MCCFIOperation::DefCFA: {
+ auto Register = registerNumber(Operands[0]);
+ if (!Register)
+ return std::unexpected(Register.error());
+ if (Operands[1] < 0) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "DWARF CFA offset cannot be negative");
+ }
+ Bytes.push_back(DWCFADefCFA);
+ appendULEB128(Bytes, *Register);
+ appendULEB128(Bytes, static_cast<std::uint64_t>(Operands[1]));
+ State = CFAState{.Register = *Register, .Offset = Operands[1]};
+ break;
+ }
+ case MCCFIOperation::AdjustCFAOffset: {
+ std::int64_t Adjusted = 0;
+ if (__builtin_add_overflow(State.Offset, Operands[0], &Adjusted) || Adjusted < 0) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "adjusted DWARF CFA offset is invalid");
+ }
+ Bytes.push_back(DWCFADefCFAOffset);
+ appendULEB128(Bytes, static_cast<std::uint64_t>(Adjusted));
+ State.Offset = Adjusted;
+ break;
+ }
+ case MCCFIOperation::Escape:
+ Bytes.insert(Bytes.end(), Entry.Instruction.Bytes.begin(), Entry.Instruction.Bytes.end());
+ break;
+ case MCCFIOperation::Restore: {
+ auto Register = registerNumber(Operands[0]);
+ if (!Register)
+ return std::unexpected(Register.error());
+ appendRestoreRule(Bytes, *Register);
+ break;
+ }
+ case MCCFIOperation::Undefined: {
+ auto Register = registerNumber(Operands[0]);
+ if (!Register)
+ return std::unexpected(Register.error());
+ Bytes.push_back(DWCFAUndefined);
+ appendULEB128(Bytes, *Register);
+ break;
+ }
+ case MCCFIOperation::Register: {
+ auto Register = registerNumber(Operands[0]);
+ auto Source = registerNumber(Operands[1]);
+ if (!Register)
+ return std::unexpected(Register.error());
+ if (!Source)
+ return std::unexpected(Source.error());
+ Bytes.push_back(DWCFARegister);
+ appendULEB128(Bytes, *Register);
+ appendULEB128(Bytes, *Source);
+ break;
+ }
+ case MCCFIOperation::WindowSave:
+ Bytes.push_back(DWCFAGNUWindowSave);
+ break;
+ case MCCFIOperation::GNUArgsSize:
+ if (Operands[0] < 0) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "DWARF GNU argument size cannot be negative");
+ }
+ Bytes.push_back(DWCFAGNUArgsSize);
+ appendULEB128(Bytes, static_cast<std::uint64_t>(Operands[0]));
+ break;
+ case MCCFIOperation::ValueOffset: {
+ auto Register = registerNumber(Operands[0]);
+ if (!Register)
+ return std::unexpected(Register.error());
+ auto Factored = factoredOffset(Operands[1], Info.DataAlignment);
+ if (!Factored)
+ return std::unexpected(Factored.error());
+ Bytes.push_back(DWCFAValOffset);
+ appendULEB128(Bytes, *Register);
+ appendULEB128(Bytes, *Factored);
+ break;
+ }
+ case MCCFIOperation::StartProc:
+ case MCCFIOperation::EndProc:
+ case MCCFIOperation::Sections:
+ case MCCFIOperation::Personality:
+ case MCCFIOperation::LSDA:
+ case MCCFIOperation::SignalFrame:
+ case MCCFIOperation::ReturnColumn:
+ case MCCFIOperation::NegateReturnAddressState:
+ case MCCFIOperation::NegateReturnAddressStateWithPC:
+ case MCCFIOperation::Label:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "this CFI operation cannot be encoded in an RV64GC FDE");
+ }
+ }
+ if (!StateStack.empty()) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "DWARF CFI procedure has unbalanced remember_state operations");
+ }
+ return {};
+}
+
+[[nodiscard]] Expected<void> finishEntry(std::vector<std::uint8_t> &Bytes, std::size_t EntryStart) {
+ while (Bytes.size() % 4U != 0)
+ Bytes.push_back(DWCFANop);
+ const std::size_t Length = Bytes.size() - EntryStart - 4U;
+ if (Length > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported, "DWARF frame entry exceeds 32 bits");
+ }
+ writeU32(Bytes, EntryStart, static_cast<std::uint32_t>(Length));
+ return {};
+}
+
+} // namespace
+
+Expected<void> MCDwarfFrameInfo::verify() const {
+ if (CodeAlignment == 0 || DataAlignment == 0) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "DWARF frame alignment factors must be nonzero");
+ }
+ if (InitialCFAOffset > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "initial DWARF CFA offset is too large");
+ }
+ return {};
+}
+
+Expected<MCObjectSection> emitDwarfEHFrame(MCSectionRef Section,
+ std::span<const MCDwarfFrameProcedure> Procedures,
+ const MCDwarfFrameInfo &Info,
+ const MCAsmBackend &Backend) {
+ if (!Section || Section->description().Kind != MCSectionKind::EHFrame) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "DWARF EH frame emission requires an EHFrame section");
+ }
+ auto Verification = Info.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+
+ MCObjectSection Result{.Section = std::move(Section),
+ .Contents = {},
+ .AddressSize = 0,
+ .Alignment = 1,
+ .Relocations = {}};
+ Result.Alignment = Result.Section->description().Alignment;
+ const std::size_t CIEStart = Result.Contents.size();
+ appendU32(Result.Contents, 0);
+ appendU32(Result.Contents, 0);
+ Result.Contents.push_back(1);
+ Result.Contents.insert(Result.Contents.end(), {'z', 'R', 0});
+ appendULEB128(Result.Contents, Info.CodeAlignment);
+ appendSLEB128(Result.Contents, Info.DataAlignment);
+ appendULEB128(Result.Contents, Info.ReturnAddressRegister);
+ appendULEB128(Result.Contents, 1);
+ Result.Contents.push_back(DWEHPEPCRelativeSData4);
+ Result.Contents.push_back(DWCFADefCFA);
+ appendULEB128(Result.Contents, Info.InitialCFARegister);
+ appendULEB128(Result.Contents, Info.InitialCFAOffset);
+ auto FinishedCIE = finishEntry(Result.Contents, CIEStart);
+ if (!FinishedCIE)
+ return std::unexpected(FinishedCIE.error());
+
+ for (const MCDwarfFrameProcedure &Procedure : Procedures) {
+ if (!Procedure.Function) {
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "DWARF frame procedure has no function symbol");
+ }
+ if (Procedure.AddressRange > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported, "DWARF FDE address range exceeds 32 bits");
+ }
+ const std::size_t FDEStart = Result.Contents.size();
+ appendU32(Result.Contents, 0);
+ const std::size_t CIEPointerOffset = Result.Contents.size();
+ const std::size_t CIEDistance = CIEPointerOffset - CIEStart;
+ if (CIEDistance > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported, "DWARF FDE CIE distance exceeds 32 bits");
+ }
+ appendU32(Result.Contents, static_cast<std::uint32_t>(CIEDistance));
+ const std::size_t InitialLocationOffset = Result.Contents.size();
+ appendU32(Result.Contents, 0);
+ appendU32(Result.Contents, static_cast<std::uint32_t>(Procedure.AddressRange));
+ appendULEB128(Result.Contents, 0);
+ auto Instructions = appendFrameInstructions(Result.Contents, Procedure.Instructions, Info);
+ if (!Instructions)
+ return std::unexpected(Instructions.error());
+ auto FinishedFDE = finishEntry(Result.Contents, FDEStart);
+ if (!FinishedFDE)
+ return std::unexpected(FinishedFDE.error());
+
+ auto Expression = MCExpr::symbol(Procedure.Function);
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ MCFixup Fixup{.Offset = InitialLocationOffset,
+ .Value = *Expression,
+ .Kind = fixup_kind::PCRelative4,
+ .PCRelative = true,
+ .LinkerRelaxable = false};
+ MCValue Value{
+ .AddSymbol = Procedure.Function, .SubtractSymbol = {}, .Addend = 0, .TargetKind = 0};
+ auto Relocation = Backend.relocationType(Fixup, Value);
+ if (!Relocation)
+ return std::unexpected(Relocation.error());
+ Result.Relocations.push_back(MCObjectRelocation{
+ .Offset = InitialLocationOffset,
+ .Type = *Relocation,
+ .Symbol = Procedure.Function,
+ .Addend = 0,
+ });
+ }
+ Result.AddressSize = Result.Contents.size();
+ return Result;
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCExpr.cpp b/backend/lib/MC/MCExpr.cpp
new file mode 100644
index 0000000..1f0bdb6
--- /dev/null
+++ b/backend/lib/MC/MCExpr.cpp
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCExpr.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace rcc::mc {
+namespace {
+
+[[nodiscard]] std::optional<std::int64_t> checkedUnary(MCUnaryOperator Operator,
+ std::int64_t Value) noexcept {
+ switch (Operator) {
+ case MCUnaryOperator::Plus:
+ return Value;
+ case MCUnaryOperator::Minus:
+ if (Value == std::numeric_limits<std::int64_t>::min())
+ return std::nullopt;
+ return -Value;
+ case MCUnaryOperator::BitwiseNot:
+ return ~Value;
+ case MCUnaryOperator::LogicalNot:
+ return Value == 0 ? 1 : 0;
+ }
+ return std::nullopt;
+}
+
+[[nodiscard]] std::optional<std::int64_t>
+checkedBinary(MCBinaryOperator Operator, std::int64_t Left, std::int64_t Right) noexcept {
+ std::int64_t Result = 0;
+ switch (Operator) {
+ case MCBinaryOperator::Add:
+ return __builtin_add_overflow(Left, Right, &Result) ? std::nullopt : std::optional(Result);
+ case MCBinaryOperator::Subtract:
+ return __builtin_sub_overflow(Left, Right, &Result) ? std::nullopt : std::optional(Result);
+ case MCBinaryOperator::Multiply:
+ return __builtin_mul_overflow(Left, Right, &Result) ? std::nullopt : std::optional(Result);
+ case MCBinaryOperator::Divide:
+ if (Right == 0 || (Left == std::numeric_limits<std::int64_t>::min() && Right == -1))
+ return std::nullopt;
+ return Left / Right;
+ case MCBinaryOperator::Modulo:
+ if (Right == 0 || (Left == std::numeric_limits<std::int64_t>::min() && Right == -1))
+ return std::nullopt;
+ return Left % Right;
+ case MCBinaryOperator::ShiftLeft:
+ if (Right < 0 || Right >= 64)
+ return std::nullopt;
+ if (Right == 63) {
+ if (Left == 0)
+ return 0;
+ return Left == -1 ? std::optional(std::numeric_limits<std::int64_t>::min()) : std::nullopt;
+ }
+ if (__builtin_mul_overflow(Left, std::int64_t{1} << Right, &Result))
+ return std::nullopt;
+ return Result;
+ case MCBinaryOperator::ShiftRight:
+ if (Right < 0 || Right >= 64)
+ return std::nullopt;
+ return Left >> Right;
+ case MCBinaryOperator::BitwiseAnd:
+ return Left & Right;
+ case MCBinaryOperator::BitwiseOr:
+ return Left | Right;
+ case MCBinaryOperator::BitwiseXor:
+ return Left ^ Right;
+ case MCBinaryOperator::LogicalAnd:
+ return Left != 0 && Right != 0 ? 1 : 0;
+ case MCBinaryOperator::LogicalOr:
+ return Left != 0 || Right != 0 ? 1 : 0;
+ case MCBinaryOperator::Equal:
+ return Left == Right ? 1 : 0;
+ case MCBinaryOperator::NotEqual:
+ return Left != Right ? 1 : 0;
+ case MCBinaryOperator::Less:
+ return Left < Right ? 1 : 0;
+ case MCBinaryOperator::LessEqual:
+ return Left <= Right ? 1 : 0;
+ case MCBinaryOperator::Greater:
+ return Left > Right ? 1 : 0;
+ case MCBinaryOperator::GreaterEqual:
+ return Left >= Right ? 1 : 0;
+ }
+ return std::nullopt;
+}
+
+[[nodiscard]] Expected<void> verifyExpression(const MCExpr &Expression, std::size_t Depth,
+ std::size_t MaximumDepth) {
+ if (Depth > MaximumDepth)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC expression exceeds its depth limit");
+
+ switch (Expression.kind()) {
+ case MCExpr::Kind::Constant:
+ if (Expression.getIf<std::int64_t>() == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "MC constant expression storage is invalid");
+ return {};
+ case MCExpr::Kind::SymbolRef: {
+ const auto *Reference = Expression.getIf<MCSymbolRefExpr>();
+ if (Reference == nullptr || !Reference->Symbol || Reference->Symbol->name().empty()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "MC symbol expression requires a valid symbol");
+ }
+ return {};
+ }
+ case MCExpr::Kind::Unary: {
+ const auto *Unary = Expression.getIf<MCUnaryExpr>();
+ if (Unary == nullptr || !Unary->Operand)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC unary expression has no operand");
+ return verifyExpression(*Unary->Operand, Depth + 1, MaximumDepth);
+ }
+ case MCExpr::Kind::Binary: {
+ const auto *Binary = Expression.getIf<MCBinaryExpr>();
+ if (Binary == nullptr || !Binary->Left || !Binary->Right)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC binary expression has a null operand");
+ auto Left = verifyExpression(*Binary->Left, Depth + 1, MaximumDepth);
+ if (!Left)
+ return std::unexpected(Left.error());
+ return verifyExpression(*Binary->Right, Depth + 1, MaximumDepth);
+ }
+ case MCExpr::Kind::Target: {
+ const auto *Target = Expression.getIf<MCTargetExpr>();
+ if (Target == nullptr || Target->Kind == 0 || !Target->Operand) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "target MC expression requires a nonzero kind and an operand");
+ }
+ return verifyExpression(*Target->Operand, Depth + 1, MaximumDepth);
+ }
+ }
+ return makeUnexpected(ErrorCode::InternalError, "MC expression kind is invalid");
+}
+
+} // namespace
+
+MCExprRef MCExpr::constant(std::int64_t Value) {
+ return std::shared_ptr<const MCExpr>(new MCExpr(Kind::Constant, Value));
+}
+
+Expected<MCExprRef> MCExpr::symbol(MCSymbolRef Symbol) {
+ if (!Symbol || Symbol->name().empty())
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC symbol expression requires a symbol");
+ return std::shared_ptr<const MCExpr>(
+ new MCExpr(Kind::SymbolRef, MCSymbolRefExpr{.Symbol = std::move(Symbol)}));
+}
+
+Expected<MCExprRef> MCExpr::unary(MCUnaryOperator Operator, MCExprRef Operand) {
+ if (!Operand)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC unary expression requires an operand");
+ return std::shared_ptr<const MCExpr>(
+ new MCExpr(Kind::Unary, MCUnaryExpr{.Operator = Operator, .Operand = std::move(Operand)}));
+}
+
+Expected<MCExprRef> MCExpr::binary(MCBinaryOperator Operator, MCExprRef Left, MCExprRef Right) {
+ if (!Left || !Right)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC binary expression requires two operands");
+ return std::shared_ptr<const MCExpr>(new MCExpr(
+ Kind::Binary,
+ MCBinaryExpr{.Operator = Operator, .Left = std::move(Left), .Right = std::move(Right)}));
+}
+
+Expected<MCExprRef> MCExpr::target(std::uint32_t Kind, MCExprRef Operand) {
+ if (Kind == 0 || !Operand) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "target MC expression requires a nonzero kind and an operand");
+ }
+ return std::shared_ptr<const MCExpr>(
+ new MCExpr(MCExpr::Kind::Target, MCTargetExpr{.Kind = Kind, .Operand = std::move(Operand)}));
+}
+
+Expected<void> MCExpr::verify(std::size_t MaximumDepth) const {
+ if (MaximumDepth == 0)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC expression depth limit cannot be zero");
+ return verifyExpression(*this, 1, MaximumDepth);
+}
+
+std::optional<std::int64_t> MCExpr::evaluateAbsolute() const noexcept {
+ switch (ExpressionKind) {
+ case Kind::Constant:
+ return *getIf<std::int64_t>();
+ case Kind::SymbolRef:
+ case Kind::Target:
+ return std::nullopt;
+ case Kind::Unary: {
+ const auto *Unary = getIf<MCUnaryExpr>();
+ if (Unary == nullptr || !Unary->Operand)
+ return std::nullopt;
+ const auto Value = Unary->Operand->evaluateAbsolute();
+ return Value ? checkedUnary(Unary->Operator, *Value) : std::nullopt;
+ }
+ case Kind::Binary: {
+ const auto *Binary = getIf<MCBinaryExpr>();
+ if (Binary == nullptr || !Binary->Left || !Binary->Right)
+ return std::nullopt;
+ const auto Left = Binary->Left->evaluateAbsolute();
+ const auto Right = Binary->Right->evaluateAbsolute();
+ return Left && Right ? checkedBinary(Binary->Operator, *Left, *Right) : std::nullopt;
+ }
+ }
+ return std::nullopt;
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCFixup.cpp b/backend/lib/MC/MCFixup.cpp
new file mode 100644
index 0000000..8819cd4
--- /dev/null
+++ b/backend/lib/MC/MCFixup.cpp
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCFixup.h"
+
+#include <algorithm>
+#include <array>
+#include <cstdint>
+#include <limits>
+#include <utility>
+#include <vector>
+
+namespace rcc::mc {
+namespace {
+
+[[nodiscard]] Expected<std::int64_t> checkedAdd(std::int64_t Left, std::int64_t Right) {
+ std::int64_t Result = 0;
+ if (__builtin_add_overflow(Left, Right, &Result))
+ return makeUnexpected(ErrorCode::RelocationFailed, "MC expression addition overflows");
+ return Result;
+}
+
+[[nodiscard]] Expected<std::int64_t> checkedSubtract(std::int64_t Left, std::int64_t Right) {
+ std::int64_t Result = 0;
+ if (__builtin_sub_overflow(Left, Right, &Result))
+ return makeUnexpected(ErrorCode::RelocationFailed, "MC expression subtraction overflows");
+ return Result;
+}
+
+[[nodiscard]] Expected<MCValue> combineLinearValues(const MCValue &Left, const MCValue &Right,
+ bool SubtractRight) {
+ struct SymbolTerm final {
+ MCSymbolRef Symbol;
+ int Coefficient = 0;
+ };
+ std::vector<SymbolTerm> Terms;
+ const auto AddTerm = [&Terms](const MCSymbolRef &Symbol, int Coefficient) -> Expected<void> {
+ if (!Symbol)
+ return {};
+ const auto Position = std::ranges::find(Terms, Symbol, &SymbolTerm::Symbol);
+ if (Position == Terms.end()) {
+ Terms.push_back(SymbolTerm{.Symbol = Symbol, .Coefficient = Coefficient});
+ return {};
+ }
+ Position->Coefficient += Coefficient;
+ if (Position->Coefficient < -1 || Position->Coefficient > 1) {
+ return makeUnexpected(ErrorCode::RelocationFailed,
+ "MC expression has a symbol coefficient outside -1 to 1");
+ }
+ if (Position->Coefficient == 0)
+ Terms.erase(Position);
+ return {};
+ };
+
+ for (const auto &[Symbol, Coefficient] : std::array<std::pair<MCSymbolRef, int>, 4>{
+ std::pair{Left.AddSymbol, 1},
+ std::pair{Left.SubtractSymbol, -1},
+ std::pair{Right.AddSymbol, SubtractRight ? -1 : 1},
+ std::pair{Right.SubtractSymbol, SubtractRight ? 1 : -1},
+ }) {
+ auto Added = AddTerm(Symbol, Coefficient);
+ if (!Added)
+ return std::unexpected(Added.error());
+ }
+
+ MCValue Result;
+ for (const SymbolTerm &Term : Terms) {
+ MCSymbolRef &Destination = Term.Coefficient > 0 ? Result.AddSymbol : Result.SubtractSymbol;
+ if (Destination) {
+ return makeUnexpected(ErrorCode::RelocationFailed,
+ "MC expression contains more than one symbol with the same sign");
+ }
+ Destination = Term.Symbol;
+ }
+ auto Addend = SubtractRight ? checkedSubtract(Left.Addend, Right.Addend)
+ : checkedAdd(Left.Addend, Right.Addend);
+ if (!Addend)
+ return std::unexpected(Addend.error());
+ Result.Addend = *Addend;
+ return Result;
+}
+
+[[nodiscard]] Expected<MCValue> evaluate(const MCExpr &Expression, std::size_t Depth) {
+ if (Depth > 256)
+ return makeUnexpected(ErrorCode::RelocationFailed, "MC expression exceeds its depth limit");
+
+ switch (Expression.kind()) {
+ case MCExpr::Kind::Constant:
+ return MCValue{.AddSymbol = {},
+ .SubtractSymbol = {},
+ .Addend = *Expression.getIf<std::int64_t>(),
+ .TargetKind = 0};
+ case MCExpr::Kind::SymbolRef:
+ return MCValue{.AddSymbol = Expression.getIf<MCSymbolRefExpr>()->Symbol,
+ .SubtractSymbol = {},
+ .Addend = 0,
+ .TargetKind = 0};
+ case MCExpr::Kind::Target: {
+ const auto &Target = *Expression.getIf<MCTargetExpr>();
+ auto Result = evaluate(*Target.Operand, Depth + 1);
+ if (!Result)
+ return std::unexpected(Result.error());
+ if (Result->TargetKind != 0) {
+ return makeUnexpected(ErrorCode::RelocationFailed,
+ "nested target MC expressions are not relocatable");
+ }
+ Result->TargetKind = Target.Kind;
+ return Result;
+ }
+ case MCExpr::Kind::Unary: {
+ const auto &Unary = *Expression.getIf<MCUnaryExpr>();
+ auto Operand = evaluate(*Unary.Operand, Depth + 1);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ if (Unary.Operator == MCUnaryOperator::Plus)
+ return Operand;
+ if (Unary.Operator == MCUnaryOperator::Minus && Operand->isAbsolute()) {
+ if (Operand->Addend == std::numeric_limits<std::int64_t>::min()) {
+ return makeUnexpected(ErrorCode::RelocationFailed, "MC expression negation overflows");
+ }
+ Operand->Addend = -Operand->Addend;
+ return Operand;
+ }
+ const auto Absolute = Expression.evaluateAbsolute();
+ if (!Absolute) {
+ return makeUnexpected(ErrorCode::RelocationFailed,
+ "symbolic unary MC expression is not relocatable");
+ }
+ return MCValue{.AddSymbol = {}, .SubtractSymbol = {}, .Addend = *Absolute, .TargetKind = 0};
+ }
+ case MCExpr::Kind::Binary: {
+ const auto &Binary = *Expression.getIf<MCBinaryExpr>();
+ if (Binary.Operator != MCBinaryOperator::Add && Binary.Operator != MCBinaryOperator::Subtract) {
+ const auto Absolute = Expression.evaluateAbsolute();
+ if (!Absolute) {
+ return makeUnexpected(ErrorCode::RelocationFailed,
+ "symbolic binary MC expression is not relocatable");
+ }
+ return MCValue{.AddSymbol = {}, .SubtractSymbol = {}, .Addend = *Absolute, .TargetKind = 0};
+ }
+ auto Left = evaluate(*Binary.Left, Depth + 1);
+ auto Right = evaluate(*Binary.Right, Depth + 1);
+ if (!Left)
+ return std::unexpected(Left.error());
+ if (!Right)
+ return std::unexpected(Right.error());
+ if (Left->TargetKind != 0 || Right->TargetKind != 0) {
+ return makeUnexpected(ErrorCode::RelocationFailed,
+ "target MC expression cannot participate in outer arithmetic");
+ }
+
+ return combineLinearValues(*Left, *Right, Binary.Operator == MCBinaryOperator::Subtract);
+ }
+ }
+ return makeUnexpected(ErrorCode::InternalError, "MC expression has an invalid kind");
+}
+
+} // namespace
+
+Expected<void> MCFixup::verify() const {
+ if (Kind == fixup_kind::None)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC fixup has no kind");
+ if (!Value)
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC fixup has no value expression");
+ auto Verification = Value->verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ return {};
+}
+
+Expected<MCValue> evaluateAsRelocatable(const MCExpr &Expression) {
+ auto Verification = Expression.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ return evaluate(Expression, 0);
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCInstPrinter.cpp b/backend/lib/MC/MCInstPrinter.cpp
new file mode 100644
index 0000000..1af6e4e
--- /dev/null
+++ b/backend/lib/MC/MCInstPrinter.cpp
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCInstPrinter.h"
+
+#include "rcc/MC/MCExpr.h"
+
+#include <cctype>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <string_view>
+
+namespace rcc::mc {
+namespace {
+
+[[nodiscard]] bool isBareSymbolName(std::string_view Name) noexcept {
+ if (Name.empty())
+ return false;
+ const auto Initial = [](unsigned char Character) {
+ return std::isalpha(Character) != 0 || Character == '_' || Character == '.' || Character == '$';
+ };
+ const auto Following = [](unsigned char Character) {
+ return std::isalnum(Character) != 0 || Character == '_' || Character == '.' || Character == '$';
+ };
+ if (!Initial(static_cast<unsigned char>(Name.front())))
+ return false;
+ for (const char Character : Name.substr(1)) {
+ if (!Following(static_cast<unsigned char>(Character)))
+ return false;
+ }
+ return true;
+}
+
+[[nodiscard]] std::string_view unarySpelling(MCUnaryOperator Operator) noexcept {
+ switch (Operator) {
+ case MCUnaryOperator::Plus:
+ return "+";
+ case MCUnaryOperator::Minus:
+ return "-";
+ case MCUnaryOperator::BitwiseNot:
+ return "~";
+ case MCUnaryOperator::LogicalNot:
+ return "!";
+ }
+ return {};
+}
+
+[[nodiscard]] std::string_view binarySpelling(MCBinaryOperator Operator) noexcept {
+ switch (Operator) {
+ case MCBinaryOperator::Add:
+ return "+";
+ case MCBinaryOperator::Subtract:
+ return "-";
+ case MCBinaryOperator::Multiply:
+ return "*";
+ case MCBinaryOperator::Divide:
+ return "/";
+ case MCBinaryOperator::Modulo:
+ return "%";
+ case MCBinaryOperator::ShiftLeft:
+ return "<<";
+ case MCBinaryOperator::ShiftRight:
+ return ">>";
+ case MCBinaryOperator::BitwiseAnd:
+ return "&";
+ case MCBinaryOperator::BitwiseOr:
+ return "|";
+ case MCBinaryOperator::BitwiseXor:
+ return "^";
+ case MCBinaryOperator::LogicalAnd:
+ return "&&";
+ case MCBinaryOperator::LogicalOr:
+ return "||";
+ case MCBinaryOperator::Equal:
+ return "==";
+ case MCBinaryOperator::NotEqual:
+ return "!=";
+ case MCBinaryOperator::Less:
+ return "<";
+ case MCBinaryOperator::LessEqual:
+ return "<=";
+ case MCBinaryOperator::Greater:
+ return ">";
+ case MCBinaryOperator::GreaterEqual:
+ return ">=";
+ }
+ return {};
+}
+
+[[nodiscard]] Expected<std::string>
+printExpressionImpl(const MCExpr &Expression, const MCInstPrinter &Printer, std::size_t Depth) {
+ if (Depth > 256)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "MC expression exceeds the assembly printing depth limit");
+
+ switch (Expression.kind()) {
+ case MCExpr::Kind::Constant:
+ return std::to_string(*Expression.getIf<std::int64_t>());
+ case MCExpr::Kind::SymbolRef: {
+ const auto *Reference = Expression.getIf<MCSymbolRefExpr>();
+ if (Reference == nullptr || !Reference->Symbol)
+ return makeUnexpected(ErrorCode::InternalError, "MC symbol expression storage is invalid");
+ return printMCSymbolName(Reference->Symbol->name());
+ }
+ case MCExpr::Kind::Unary: {
+ const auto *Unary = Expression.getIf<MCUnaryExpr>();
+ if (Unary == nullptr || !Unary->Operand)
+ return makeUnexpected(ErrorCode::InternalError, "MC unary expression storage is invalid");
+ auto Operand = printExpressionImpl(*Unary->Operand, Printer, Depth + 1);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ return std::string(unarySpelling(Unary->Operator)) + "(" + *Operand + ")";
+ }
+ case MCExpr::Kind::Binary: {
+ const auto *Binary = Expression.getIf<MCBinaryExpr>();
+ if (Binary == nullptr || !Binary->Left || !Binary->Right)
+ return makeUnexpected(ErrorCode::InternalError, "MC binary expression storage is invalid");
+ auto Left = printExpressionImpl(*Binary->Left, Printer, Depth + 1);
+ if (!Left)
+ return std::unexpected(Left.error());
+ auto Right = printExpressionImpl(*Binary->Right, Printer, Depth + 1);
+ if (!Right)
+ return std::unexpected(Right.error());
+ return "(" + *Left + std::string(binarySpelling(Binary->Operator)) + *Right + ")";
+ }
+ case MCExpr::Kind::Target: {
+ const auto *Target = Expression.getIf<MCTargetExpr>();
+ if (Target == nullptr || !Target->Operand)
+ return makeUnexpected(ErrorCode::InternalError, "target MC expression storage is invalid");
+ auto Operand = printExpressionImpl(*Target->Operand, Printer, Depth + 1);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ return Printer.printTargetExpression(Target->Kind, *Operand);
+ }
+ }
+ return makeUnexpected(ErrorCode::InternalError, "MC expression kind is invalid");
+}
+
+} // namespace
+
+Expected<std::string> printMCSymbolName(std::string_view Name) {
+ if (Name.empty())
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "MC symbol name cannot be empty");
+ if (isBareSymbolName(Name))
+ return std::string(Name);
+
+ std::string Result;
+ Result.push_back('"');
+ for (const char RawCharacter : Name) {
+ const auto Character = static_cast<unsigned char>(RawCharacter);
+ if (Character == 0 || Character == '\n' || Character == '\r' || Character == 0x7fU) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "MC symbol name contains a non-printable character");
+ }
+ if (Character == '"' || Character == '\\')
+ Result.push_back('\\');
+ Result.push_back(static_cast<char>(Character));
+ }
+ Result.push_back('"');
+ return Result;
+}
+
+Expected<std::string> printMCExpression(const MCExpr &Expression, const MCInstPrinter &Printer) {
+ auto Verification = Expression.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ return printExpressionImpl(Expression, Printer, 1);
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCObjectStreamer.cpp b/backend/lib/MC/MCObjectStreamer.cpp
new file mode 100644
index 0000000..634b713
--- /dev/null
+++ b/backend/lib/MC/MCObjectStreamer.cpp
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCObjectStreamer.h"
+
+#include <utility>
+
+namespace rcc::mc {
+
+Expected<void> ELFObjectStreamer::ensureOpen() const {
+ if (IsFinished)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "object stream is already finalized");
+ return {};
+}
+
+Expected<void> ELFObjectStreamer::switchSection(MCSectionRef Section) {
+ auto Open = ensureOpen();
+ return Open ? Assembler.switchSection(std::move(Section)) : Open;
+}
+
+Expected<void> ELFObjectStreamer::emitLabel(MCSymbolRef Symbol) {
+ auto Open = ensureOpen();
+ return Open ? Assembler.emitLabel(std::move(Symbol)) : Open;
+}
+
+Expected<void> ELFObjectStreamer::emitInstruction(const MCInst &Instruction) {
+ auto Open = ensureOpen();
+ return Open ? Assembler.emitInstruction(Instruction) : Open;
+}
+
+Expected<void> ELFObjectStreamer::emitValue(MCExprRef Value, std::uint8_t Size) {
+ auto Open = ensureOpen();
+ return Open ? Assembler.emitValue(std::move(Value), Size) : Open;
+}
+
+Expected<void> ELFObjectStreamer::emitBytes(std::span<const std::uint8_t> Bytes) {
+ auto Open = ensureOpen();
+ return Open ? Assembler.emitBytes(Bytes) : Open;
+}
+
+Expected<void> ELFObjectStreamer::emitZeroFill(std::uint64_t Size) {
+ auto Open = ensureOpen();
+ return Open ? Assembler.emitZeroFill(Size) : Open;
+}
+
+Expected<void> ELFObjectStreamer::emitAlignment(std::uint64_t Alignment, std::uint8_t Fill,
+ std::uint64_t MaximumBytes) {
+ auto Open = ensureOpen();
+ return Open ? Assembler.emitAlignment(Alignment, Fill, MaximumBytes) : Open;
+}
+
+Expected<void> ELFObjectStreamer::emitSymbolAttribute(MCSymbolRef Symbol,
+ MCSymbolAttribute Attribute) {
+ auto Open = ensureOpen();
+ return Open ? Assembler.emitSymbolAttribute(std::move(Symbol), Attribute) : Open;
+}
+
+Expected<void> ELFObjectStreamer::emitSymbolSize(MCSymbolRef Symbol, MCExprRef Size) {
+ auto Open = ensureOpen();
+ return Open ? Assembler.emitSymbolSize(std::move(Symbol), std::move(Size)) : Open;
+}
+
+Expected<void> ELFObjectStreamer::emitCommonSymbol(MCSymbolRef Symbol, std::uint64_t Size,
+ std::uint64_t Alignment) {
+ auto Open = ensureOpen();
+ return Open ? Assembler.emitCommonSymbol(std::move(Symbol), Size, Alignment) : Open;
+}
+
+Expected<void> ELFObjectStreamer::emitDirective(std::string_view Name, std::string_view) {
+ auto Open = ensureOpen();
+ if (!Open)
+ return Open;
+ if (Name == "attribute")
+ return {};
+ return makeUnexpected(ErrorCode::Unsupported,
+ "native object emission does not support the MC directive '" +
+ std::string(Name) + "'");
+}
+
+Expected<void> ELFObjectStreamer::emitCFI(const MCCFIInstruction &Instruction) {
+ auto Open = ensureOpen();
+ if (!Open)
+ return Open;
+ return Assembler.emitCFI(Instruction);
+}
+
+Expected<void> ELFObjectStreamer::emitInlineAssembly(std::string_view Text) {
+ auto Open = ensureOpen();
+ if (!Open)
+ return Open;
+ if (Text.empty())
+ return {};
+ return makeUnexpected(ErrorCode::Unsupported,
+ "native object emission requires parsed inline assembly");
+}
+
+Expected<void> ELFObjectStreamer::emitComment(std::string_view) {
+ return ensureOpen();
+}
+
+Expected<void> ELFObjectStreamer::configureDwarfFrame(MCSectionRef Section, MCDwarfFrameInfo Info) {
+ auto Open = ensureOpen();
+ if (!Open)
+ return Open;
+ return Assembler.configureDwarfFrame(std::move(Section), Info);
+}
+
+Expected<void> ELFObjectStreamer::finish() {
+ auto Open = ensureOpen();
+ if (!Open)
+ return Open;
+ auto Object = Assembler.assemble();
+ if (!Object)
+ return std::unexpected(Object.error());
+ auto Written = Writer->writeObject(*Object);
+ if (!Written)
+ return std::unexpected(Written.error());
+ Output = std::move(*Written);
+ IsFinished = true;
+ return {};
+}
+
+Expected<std::vector<std::uint8_t>> ELFObjectStreamer::takeOutput() {
+ if (!IsFinished)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed,
+ "object output cannot be taken before finalization");
+ if (OutputWasTaken)
+ return makeUnexpected(ErrorCode::ObjectWritingFailed, "object output was already taken");
+ OutputWasTaken = true;
+ return std::move(Output);
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCSection.cpp b/backend/lib/MC/MCSection.cpp
new file mode 100644
index 0000000..464dcc6
--- /dev/null
+++ b/backend/lib/MC/MCSection.cpp
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCSection.h"
+
+#include <bit>
+#include <cstdint>
+#include <string_view>
+
+namespace rcc::mc {
+namespace {
+
+[[nodiscard]] bool isValidAssemblyName(std::string_view Name) noexcept {
+ if (Name.empty())
+ return false;
+ for (const char RawCharacter : Name) {
+ const auto Character = static_cast<unsigned char>(RawCharacter);
+ if (Character == 0 || Character == '\n' || Character == '\r' || Character == 0x7fU)
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+Expected<void> MCSectionDesc::verify() const {
+ if (!isValidAssemblyName(Name))
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC section has an invalid name");
+ if (!std::has_single_bit(Alignment)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "MC section alignment must be a nonzero power of two");
+ }
+ if (Flags.has(MCSectionFlag::Strings) && !Flags.has(MCSectionFlag::Merge)) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "string MC sections must also be mergeable");
+ }
+ if (Flags.has(MCSectionFlag::Merge) && EntrySize == 0) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "mergeable MC sections require a nonzero entry size");
+ }
+ if ((Kind == MCSectionKind::TLSData || Kind == MCSectionKind::TLSBSS) &&
+ !Flags.has(MCSectionFlag::TLS)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "TLS MC sections must carry the TLS section flag");
+ }
+ if (Flags.has(MCSectionFlag::Group) != static_cast<bool>(Group)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "MC section group flag and group symbol must agree");
+ }
+ if (Group && Group->name().empty()) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC section group symbol must have a name");
+ }
+ if (UniqueID && *UniqueID == 0) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "unique MC section identity must be nonzero");
+ }
+ return {};
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/MCStreamer.cpp b/backend/lib/MC/MCStreamer.cpp
new file mode 100644
index 0000000..709dccc
--- /dev/null
+++ b/backend/lib/MC/MCStreamer.cpp
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCStreamer.h"
+
+#include <algorithm>
+#include <array>
+#include <bit>
+#include <cctype>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <ranges>
+#include <span>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace rcc::mc {
+namespace {
+
+[[nodiscard]] bool containsInvalidLineCharacter(std::string_view Text) noexcept {
+ return Text.find('\0') != std::string_view::npos || Text.find('\n') != std::string_view::npos ||
+ Text.find('\r') != std::string_view::npos;
+}
+
+[[nodiscard]] unsigned log2Exact(std::uint64_t Value) noexcept {
+ unsigned Result = 0;
+ while (Value > 1) {
+ Value >>= 1U;
+ ++Result;
+ }
+ return Result;
+}
+
+[[nodiscard]] std::string byteSpelling(std::uint8_t Byte) {
+ constexpr std::string_view Hex = "0123456789abcdef";
+ std::string Result = "0x00";
+ Result[2] = Hex[(Byte >> 4U) & 0xfU];
+ Result[3] = Hex[Byte & 0xfU];
+ return Result;
+}
+
+[[nodiscard]] std::string sectionFlagSpelling(MCSectionFlags Flags) {
+ std::string Result;
+ const auto Add = [&](MCSectionFlag Flag, char Spelling) {
+ if (Flags.has(Flag))
+ Result.push_back(Spelling);
+ };
+ Add(MCSectionFlag::Alloc, 'a');
+ Add(MCSectionFlag::Writable, 'w');
+ Add(MCSectionFlag::Executable, 'x');
+ Add(MCSectionFlag::Merge, 'M');
+ Add(MCSectionFlag::Strings, 'S');
+ Add(MCSectionFlag::TLS, 'T');
+ Add(MCSectionFlag::Group, 'G');
+ Add(MCSectionFlag::Retain, 'R');
+ return Result;
+}
+
+[[nodiscard]] std::string_view sectionTypeSpelling(MCSectionKind Kind) noexcept {
+ switch (Kind) {
+ case MCSectionKind::BSS:
+ case MCSectionKind::TLSBSS:
+ return "nobits";
+ case MCSectionKind::Text:
+ case MCSectionKind::ReadOnly:
+ case MCSectionKind::Data:
+ case MCSectionKind::TLSData:
+ case MCSectionKind::EHFrame:
+ case MCSectionKind::Custom:
+ return "progbits";
+ }
+ return {};
+}
+
+[[nodiscard]] bool isDirectiveName(std::string_view Name) noexcept {
+ if (Name.empty() || Name.front() == '.')
+ return false;
+ const auto Initial = [](unsigned char Character) {
+ return std::isalpha(Character) != 0 || Character == '_';
+ };
+ const auto Following = [](unsigned char Character) {
+ return std::isalnum(Character) != 0 || Character == '_' || Character == '.';
+ };
+ if (!Initial(static_cast<unsigned char>(Name.front())))
+ return false;
+ return std::ranges::all_of(Name.substr(1), [Following](char Character) {
+ return Following(static_cast<unsigned char>(Character));
+ });
+}
+
+[[nodiscard]] std::string joinIntegerOperands(std::span<const std::int64_t> Operands) {
+ std::string Result;
+ for (const std::int64_t Operand : Operands) {
+ if (!Result.empty())
+ Result += ", ";
+ Result += std::to_string(Operand);
+ }
+ return Result;
+}
+
+[[nodiscard]] std::vector<std::uint8_t> encodeULEB128(std::uint64_t Value) {
+ std::vector<std::uint8_t> Bytes;
+ do {
+ std::uint8_t Byte = static_cast<std::uint8_t>(Value & 0x7fU);
+ Value >>= 7U;
+ if (Value != 0)
+ Byte |= 0x80U;
+ Bytes.push_back(Byte);
+ } while (Value != 0);
+ return Bytes;
+}
+
+} // namespace
+
+Expected<void> AsmTextStreamer::appendLine(std::string_view Prefix, std::string_view Text) {
+ if (IsFinished)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "cannot emit after the assembly stream is finalized");
+ if (Text.empty() || containsInvalidLineCharacter(Text)) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "assembly stream line must be nonempty and contain no line break");
+ }
+ Output.append(Prefix);
+ Output.append(Text);
+ Output.push_back('\n');
+ return {};
+}
+
+Expected<void> AsmTextStreamer::requireActiveSection() const {
+ if (!CurrentSection) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "assembly emission requires an active section");
+ }
+ if (IsFinished) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "cannot emit after the assembly stream is finalized");
+ }
+ return {};
+}
+
+Expected<void> AsmTextStreamer::switchSection(MCSectionRef Section) {
+ if (IsFinished)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "cannot switch section after assembly finalization");
+ if (!Section)
+ return makeUnexpected(ErrorCode::InvalidArgument, "cannot switch to a null MC section");
+ auto Verification = Section->description().verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ if (CurrentSection == Section)
+ return {};
+
+ auto Name = printMCSymbolName(Section->name());
+ if (!Name)
+ return std::unexpected(Name.error());
+ const MCSectionDesc &Description = Section->description();
+ std::string Directive = ".section " + *Name + ",\"" + sectionFlagSpelling(Description.Flags) +
+ "\",@" + std::string(sectionTypeSpelling(Description.Kind));
+ if (Description.EntrySize != 0)
+ Directive += "," + std::to_string(Description.EntrySize);
+ if (Description.Group) {
+ auto GroupName = printMCSymbolName(Description.Group->name());
+ if (!GroupName)
+ return std::unexpected(GroupName.error());
+ Directive += "," + *GroupName + ",comdat";
+ }
+ if (Description.UniqueID)
+ Directive += ",unique," + std::to_string(*Description.UniqueID);
+
+ auto Emitted = appendLine({}, Directive);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ CurrentSection = std::move(Section);
+ return {};
+}
+
+Expected<void> AsmTextStreamer::emitLabel(MCSymbolRef Symbol) {
+ auto Active = requireActiveSection();
+ if (!Active)
+ return std::unexpected(Active.error());
+ if (!Symbol)
+ return makeUnexpected(ErrorCode::InvalidArgument, "cannot emit a null MC symbol label");
+ const std::string Name(Symbol->name());
+ if (!DefinedSymbols.insert(Name).second) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "MC symbol label is defined more than once");
+ }
+ auto Printed = printMCSymbolName(Name);
+ if (!Printed)
+ return std::unexpected(Printed.error());
+ return appendLine({}, *Printed + ":");
+}
+
+Expected<void> AsmTextStreamer::emitInstruction(const MCInst &Instruction) {
+ auto Active = requireActiveSection();
+ if (!Active)
+ return std::unexpected(Active.error());
+ if (!Instruction.opcode().isValid())
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "MC instruction has an invalid opcode");
+ for (const MCOperand &Operand : Instruction.operands()) {
+ if (const MCExprRef *Expression = Operand.expressionValue(); Expression != nullptr) {
+ if (!*Expression)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "MC instruction contains a null expression operand");
+ auto Verification = (*Expression)->verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ }
+ }
+ auto Text = Printer->printInstruction(Instruction);
+ if (!Text)
+ return std::unexpected(Text.error());
+ return appendLine(" ", *Text);
+}
+
+Expected<void> AsmTextStreamer::emitValue(MCExprRef Value, std::uint8_t Size) {
+ auto Active = requireActiveSection();
+ if (!Active)
+ return std::unexpected(Active.error());
+ if (!Value)
+ return makeUnexpected(ErrorCode::InvalidArgument, "cannot emit a null MC value expression");
+ std::string_view Directive;
+ switch (Size) {
+ case 1:
+ Directive = ".byte ";
+ break;
+ case 2:
+ Directive = ".2byte ";
+ break;
+ case 4:
+ Directive = ".4byte ";
+ break;
+ case 8:
+ Directive = ".8byte ";
+ break;
+ default:
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "assembly value size must be 1, 2, 4, or 8 bytes");
+ }
+ auto Printed = printMCExpression(*Value, *Printer);
+ if (!Printed)
+ return std::unexpected(Printed.error());
+ return appendLine(" ", std::string(Directive) + *Printed);
+}
+
+Expected<void> AsmTextStreamer::emitBytes(std::span<const std::uint8_t> Bytes) {
+ auto Active = requireActiveSection();
+ if (!Active)
+ return std::unexpected(Active.error());
+ for (std::size_t Begin = 0; Begin < Bytes.size(); Begin += 16) {
+ const std::size_t End = std::min(Bytes.size(), Begin + 16);
+ std::string Directive = ".byte ";
+ for (std::size_t Index = Begin; Index < End; ++Index) {
+ if (Index != Begin)
+ Directive += ", ";
+ Directive += byteSpelling(Bytes[Index]);
+ }
+ auto Emitted = appendLine(" ", Directive);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ }
+ return {};
+}
+
+Expected<void> AsmTextStreamer::emitZeroFill(std::uint64_t Size) {
+ auto Active = requireActiveSection();
+ if (!Active)
+ return std::unexpected(Active.error());
+ if (Size == 0)
+ return {};
+ return appendLine(" ", ".zero " + std::to_string(Size));
+}
+
+Expected<void> AsmTextStreamer::emitAlignment(std::uint64_t Alignment, std::uint8_t Fill,
+ std::uint64_t MaximumBytes) {
+ auto Active = requireActiveSection();
+ if (!Active)
+ return std::unexpected(Active.error());
+ if (!std::has_single_bit(Alignment)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "assembly alignment must be a nonzero power of two");
+ }
+ std::string Directive = ".p2align " + std::to_string(log2Exact(Alignment));
+ if (Fill != 0 || MaximumBytes != 0)
+ Directive += ", " + std::to_string(Fill);
+ if (MaximumBytes != 0)
+ Directive += ", " + std::to_string(MaximumBytes);
+ return appendLine(" ", Directive);
+}
+
+Expected<void> AsmTextStreamer::emitSymbolAttribute(MCSymbolRef Symbol,
+ MCSymbolAttribute Attribute) {
+ if (IsFinished)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "cannot emit a symbol attribute after assembly finalization");
+ if (!Symbol)
+ return makeUnexpected(ErrorCode::InvalidArgument, "symbol attribute requires an MC symbol");
+ auto Name = printMCSymbolName(Symbol->name());
+ if (!Name)
+ return std::unexpected(Name.error());
+
+ switch (Attribute) {
+ case MCSymbolAttribute::Global:
+ return appendLine({}, ".globl " + *Name);
+ case MCSymbolAttribute::Weak:
+ return appendLine({}, ".weak " + *Name);
+ case MCSymbolAttribute::Local:
+ return appendLine({}, ".local " + *Name);
+ case MCSymbolAttribute::Hidden:
+ return appendLine({}, ".hidden " + *Name);
+ case MCSymbolAttribute::Protected:
+ return appendLine({}, ".protected " + *Name);
+ case MCSymbolAttribute::Function:
+ return appendLine({}, ".type " + *Name + ", @function");
+ case MCSymbolAttribute::Object:
+ return appendLine({}, ".type " + *Name + ", @object");
+ case MCSymbolAttribute::TLSObject:
+ return appendLine({}, ".type " + *Name + ", @tls_object");
+ }
+ return makeUnexpected(ErrorCode::InternalError, "MC symbol attribute is invalid");
+}
+
+Expected<void> AsmTextStreamer::emitSymbolSize(MCSymbolRef Symbol, MCExprRef Size) {
+ if (IsFinished)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "cannot emit a symbol size after assembly finalization");
+ if (!Symbol || !Size)
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "symbol size event requires a symbol and an expression");
+ auto Name = printMCSymbolName(Symbol->name());
+ if (!Name)
+ return std::unexpected(Name.error());
+ auto PrintedSize = printMCExpression(*Size, *Printer);
+ if (!PrintedSize)
+ return std::unexpected(PrintedSize.error());
+ return appendLine({}, ".size " + *Name + ", " + *PrintedSize);
+}
+
+Expected<void> AsmTextStreamer::emitCommonSymbol(MCSymbolRef Symbol, std::uint64_t Size,
+ std::uint64_t Alignment) {
+ if (IsFinished)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "cannot emit a common symbol after assembly finalization");
+ if (!Symbol || Size == 0 || Alignment == 0 || (Alignment & (Alignment - 1U)) != 0) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "common symbol requires a symbol, size, and power-of-two alignment");
+ }
+ auto Name = printMCSymbolName(Symbol->name());
+ if (!Name)
+ return std::unexpected(Name.error());
+ return appendLine({}, ".comm " + *Name + "," + std::to_string(Size) + "," +
+ std::to_string(Alignment));
+}
+
+Expected<void> AsmTextStreamer::emitDirective(std::string_view Name, std::string_view Arguments) {
+ if (!isDirectiveName(Name) || containsInvalidLineCharacter(Arguments)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "assembly directive name or arguments are invalid");
+ }
+ std::string Text = "." + std::string(Name);
+ if (!Arguments.empty())
+ Text += " " + std::string(Arguments);
+ return appendLine({}, Text);
+}
+
+Expected<void> AsmTextStreamer::emitCFI(const MCCFIInstruction &Instruction) {
+ auto Active = requireActiveSection();
+ if (!Active)
+ return std::unexpected(Active.error());
+ auto Verification = Instruction.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+
+ const std::string Operands = joinIntegerOperands(Instruction.Operands);
+ switch (Instruction.Operation) {
+ case MCCFIOperation::StartProc:
+ return emitDirective("cfi_startproc");
+ case MCCFIOperation::EndProc:
+ return emitDirective("cfi_endproc");
+ case MCCFIOperation::Sections:
+ return emitDirective("cfi_sections", ".eh_frame");
+ case MCCFIOperation::Personality:
+ case MCCFIOperation::LSDA: {
+ auto Symbol = printMCSymbolName(Instruction.Symbol->name());
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ return emitDirective(Instruction.Operation == MCCFIOperation::Personality ? "cfi_personality"
+ : "cfi_lsda",
+ Operands + ", " + *Symbol);
+ }
+ case MCCFIOperation::SignalFrame:
+ return emitDirective("cfi_signal_frame");
+ case MCCFIOperation::ReturnColumn:
+ return emitDirective("cfi_return_column", Operands);
+ case MCCFIOperation::SameValue:
+ return emitDirective("cfi_same_value", Operands);
+ case MCCFIOperation::RememberState:
+ return emitDirective("cfi_remember_state");
+ case MCCFIOperation::RestoreState:
+ return emitDirective("cfi_restore_state");
+ case MCCFIOperation::Offset:
+ return emitDirective("cfi_offset", Operands);
+ case MCCFIOperation::DefCFARegister:
+ return emitDirective("cfi_def_cfa_register", Operands);
+ case MCCFIOperation::DefCFAOffset:
+ return emitDirective("cfi_def_cfa_offset", Operands);
+ case MCCFIOperation::DefCFA:
+ return emitDirective("cfi_def_cfa", Operands);
+ case MCCFIOperation::RelativeOffset:
+ return emitDirective("cfi_rel_offset", Operands);
+ case MCCFIOperation::AdjustCFAOffset:
+ return emitDirective("cfi_adjust_cfa_offset", Operands);
+ case MCCFIOperation::Escape: {
+ std::string Bytes;
+ for (const std::uint8_t Byte : Instruction.Bytes) {
+ if (!Bytes.empty())
+ Bytes += ", ";
+ Bytes += byteSpelling(Byte);
+ }
+ return emitDirective("cfi_escape", Bytes);
+ }
+ case MCCFIOperation::Restore:
+ return emitDirective("cfi_restore", Operands);
+ case MCCFIOperation::Undefined:
+ return emitDirective("cfi_undefined", Operands);
+ case MCCFIOperation::Register:
+ return emitDirective("cfi_register", Operands);
+ case MCCFIOperation::WindowSave:
+ return emitDirective("cfi_window_save");
+ case MCCFIOperation::NegateReturnAddressState:
+ return emitDirective("cfi_negate_ra_state");
+ case MCCFIOperation::NegateReturnAddressStateWithPC:
+ return emitDirective("cfi_negate_ra_state_with_pc");
+ case MCCFIOperation::GNUArgsSize: {
+ if (Instruction.Operands.front() < 0) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "GNU CFI argument size cannot be negative");
+ }
+ std::vector<std::uint8_t> Bytes{0x2eU};
+ const auto Encoded = encodeULEB128(static_cast<std::uint64_t>(Instruction.Operands.front()));
+ Bytes.insert(Bytes.end(), Encoded.begin(), Encoded.end());
+ return emitCFI(MCCFIInstruction{.Operation = MCCFIOperation::Escape,
+ .Operands = {},
+ .Bytes = std::move(Bytes),
+ .Symbol = {}});
+ }
+ case MCCFIOperation::Label:
+ return emitLabel(Instruction.Symbol);
+ case MCCFIOperation::ValueOffset:
+ return emitDirective("cfi_val_offset", Operands);
+ }
+ return makeUnexpected(ErrorCode::InternalError, "MC CFI operation is invalid");
+}
+
+Expected<void> AsmTextStreamer::emitInlineAssembly(std::string_view Text) {
+ auto Active = requireActiveSection();
+ if (!Active)
+ return std::unexpected(Active.error());
+ if (Text.find('\0') != std::string_view::npos || Text.find('\r') != std::string_view::npos) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "inline assembly contains an invalid character");
+ }
+ if (Text.empty())
+ return {};
+ Output.append(Text);
+ if (Text.back() != '\n')
+ Output.push_back('\n');
+ return {};
+}
+
+Expected<void> AsmTextStreamer::emitComment(std::string_view Text) {
+ auto Active = requireActiveSection();
+ if (!Active)
+ return std::unexpected(Active.error());
+ if (Text.empty() || containsInvalidLineCharacter(Text))
+ return makeUnexpected(ErrorCode::InvalidArgument, "assembly comment is invalid");
+ return appendLine(" # ", Text);
+}
+
+Expected<void> AsmTextStreamer::finish() {
+ if (IsFinished)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "assembly stream was already finalized");
+ IsFinished = true;
+ return {};
+}
+
+Expected<std::string> AsmTextStreamer::takeOutput() {
+ if (!IsFinished)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "assembly output cannot be taken before finalization");
+ if (OutputWasTaken)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "assembly output was already taken");
+ OutputWasTaken = true;
+ return std::move(Output);
+}
+
+} // namespace rcc::mc
diff --git a/backend/lib/MC/TargetRegistry.cpp b/backend/lib/MC/TargetRegistry.cpp
new file mode 100644
index 0000000..217f59b
--- /dev/null
+++ b/backend/lib/MC/TargetRegistry.cpp
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/TargetRegistry.h"
+
+#include "rcc/Target/TargetMachine.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace rcc {
+namespace {
+
+[[nodiscard]] std::vector<const Target *> &registeredTargets() {
+ static std::vector<const Target *> Targets;
+ return Targets;
+}
+
+} // namespace
+
+TargetMachineOptions Target::defaultMachineOptions() const {
+ return DefaultOptions();
+}
+
+Expected<std::unique_ptr<TargetMachine>>
+Target::createTargetMachine(TargetMachineOptions Options) const {
+ return TargetMachineCtor(*this, std::move(Options));
+}
+
+void TargetRegistry::registerTarget(const Target &RegisteredTarget) {
+ std::vector<const Target *> &Targets = registeredTargets();
+ if (std::ranges::find(Targets, &RegisteredTarget) == Targets.end())
+ Targets.push_back(&RegisteredTarget);
+}
+
+Expected<const Target *> TargetRegistry::lookupTarget(std::string_view Triple) {
+ const Target *Best = nullptr;
+ unsigned BestQuality = 0;
+ for (const Target *Candidate : registeredTargets()) {
+ const unsigned Quality = Candidate->matchQuality(Triple);
+ if (Quality > BestQuality) {
+ Best = Candidate;
+ BestQuality = Quality;
+ } else if (Quality != 0 && Quality == BestQuality) {
+ return makeUnexpected(ErrorCode::InvalidInvocation,
+ "target triple matches multiple registered targets");
+ }
+ }
+ if (Best == nullptr) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "no registered target supports triple '" + std::string(Triple) + "'");
+ }
+ return Best;
+}
+
+Expected<const Target *> TargetRegistry::defaultTarget() {
+ const Target *Default = nullptr;
+ for (const Target *Candidate : registeredTargets()) {
+ if (!Candidate->isDefault())
+ continue;
+ if (Default != nullptr) {
+ return makeUnexpected(ErrorCode::InternalError,
+ "multiple targets are registered as the default target");
+ }
+ Default = Candidate;
+ }
+ if (Default == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "no default target is registered");
+ return Default;
+}
+
+} // namespace rcc