aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/Target/RISCV
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib/Target/RISCV')
-rw-r--r--backend/lib/Target/RISCV/CMakeLists.txt20
-rw-r--r--backend/lib/Target/RISCV/RISCVAssemblyPrinter.cpp66
-rw-r--r--backend/lib/Target/RISCV/RISCVInstPrinter.cpp279
-rw-r--r--backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp1158
4 files changed, 1522 insertions, 1 deletions
diff --git a/backend/lib/Target/RISCV/CMakeLists.txt b/backend/lib/Target/RISCV/CMakeLists.txt
index a29315a..646e856 100644
--- a/backend/lib/Target/RISCV/CMakeLists.txt
+++ b/backend/lib/Target/RISCV/CMakeLists.txt
@@ -50,7 +50,10 @@ add_library(
RISCVBranchInfo.cpp
RISCVCodeGen.cpp
RISCVCallLowering.cpp
+ RISCVAssemblyPrinter.cpp
RISCVFrameLowering.cpp
+ RISCVInstPrinter.cpp
+ RISCVMCTargetLowering.cpp
RISCVMachineCombiner.cpp
RISCVMachineMemoryFolding.cpp
RISCVOptimizationPipeline.cpp
@@ -75,7 +78,13 @@ target_include_directories(
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
PRIVATE "${RCC_BACKEND_BINARY_DIR}/generated/lib/Target/RISCV"
)
-target_link_libraries(RCCBackendRISCV PUBLIC RCCBackend::CodeGen)
+target_link_libraries(
+ RCCBackendRISCV
+ PUBLIC
+ RCCBackend::AsmPrinter
+ RCCBackend::CodeGen
+ RCCBackend::MC
+)
set_target_properties(
RCCBackendRISCV
@@ -100,9 +109,12 @@ install(
rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVBranchInfo.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/RISCVAssemblyPrinter.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVCodeGen.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVCallLowering.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVFrameLowering.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/RISCVInstPrinter.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/RISCVMCTargetLowering.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVMachineCombiner.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVMachineMemoryFolding.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVOptimizationPipeline.cpp"
@@ -111,14 +123,20 @@ rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVScheduling.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVTarget.cpp"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVCodeGen.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVAssemblyPrinter.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVCallLowering.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVInstPrinter.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVMCTargetLowering.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVTarget.h"
)
rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVBranchInfo.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/RISCVAssemblyPrinter.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVCodeGen.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVCallLowering.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVFrameLowering.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/RISCVInstPrinter.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/RISCVMCTargetLowering.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVMachineCombiner.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVMachineMemoryFolding.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVOptimizationPipeline.cpp"
diff --git a/backend/lib/Target/RISCV/RISCVAssemblyPrinter.cpp b/backend/lib/Target/RISCV/RISCVAssemblyPrinter.cpp
new file mode 100644
index 0000000..7661c61
--- /dev/null
+++ b/backend/lib/Target/RISCV/RISCVAssemblyPrinter.cpp
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/Target/RISCV/RISCVAssemblyPrinter.h"
+
+#include "rcc/CodeGen/AsmPrinter.h"
+#include "rcc/CodeGen/CompilationUnit.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineMCContext.h"
+#include "rcc/MC/MCContext.h"
+#include "rcc/MC/MCStreamer.h"
+#include "rcc/Target/RISCV/RISCVMCTargetLowering.h"
+
+#include <string>
+
+namespace rcc::riscv {
+namespace {
+
+[[nodiscard]] AsmPrinterOptions printerOptions(AssemblyEmissionOptions Options) noexcept {
+ return AsmPrinterOptions{
+ .EmitFunctionDirectives = Options.EmitFunctionDirectives,
+ .EmitCFI = Options.EmitCFI,
+ .EmitBlockLabels = Options.EmitBlockLabels,
+ .EmitInstructionSymbols = true,
+ .VerifyInput = Options.VerifyInput,
+ };
+}
+
+} // namespace
+
+Expected<std::string> emitAssembly(const CompilationUnit &Unit, const TargetDescription &Target,
+ AssemblyEmissionOptions Options) {
+ mc::MCContext MCContext;
+ MachineMCContext MachineContext(MCContext);
+ RISCVMCTargetLowering Lowering(Target);
+ mc::AsmTextStreamer Streamer(Lowering.instructionPrinter());
+ AsmPrinter Printer(Target, Lowering, MachineContext, Streamer, printerOptions(Options));
+ auto Emitted = Printer.emitCompilationUnit(Unit);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ return Streamer.takeOutput();
+}
+
+Expected<std::string> emitAssembly(const MachineFunction &Function, const TargetDescription &Target,
+ AssemblyEmissionOptions Options) {
+ mc::MCContext MCContext;
+ MachineMCContext MachineContext(MCContext);
+ RISCVMCTargetLowering Lowering(Target);
+ mc::AsmTextStreamer Streamer(Lowering.instructionPrinter());
+ AsmPrinter Printer(Target, Lowering, MachineContext, Streamer, printerOptions(Options));
+
+ auto Header = Lowering.emitFileHeader(MachineContext, Streamer);
+ if (!Header)
+ return std::unexpected(Header.error());
+ auto Emitted = Printer.emitFunction(Function);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ auto Trailer = Lowering.emitFileTrailer(MachineContext, Streamer);
+ if (!Trailer)
+ return std::unexpected(Trailer.error());
+ auto Finished = Streamer.finish();
+ if (!Finished)
+ return std::unexpected(Finished.error());
+ return Streamer.takeOutput();
+}
+
+} // namespace rcc::riscv
diff --git a/backend/lib/Target/RISCV/RISCVInstPrinter.cpp b/backend/lib/Target/RISCV/RISCVInstPrinter.cpp
new file mode 100644
index 0000000..ba3e509
--- /dev/null
+++ b/backend/lib/Target/RISCV/RISCVInstPrinter.cpp
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/Target/RISCV/RISCVInstPrinter.h"
+
+#include "rcc/CodeGen/Register.h"
+#include "rcc/MC/MCExpr.h"
+#include "rcc/MC/MCInst.h"
+#include "rcc/MC/MCInstPrinter.h"
+#include "rcc/Target/RISCV/RISCVTarget.h"
+
+#include <array>
+#include <charconv>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <ranges>
+#include <string>
+#include <string_view>
+
+namespace rcc::riscv {
+namespace {
+
+struct LinearFormat final {
+ std::string_view Name;
+ std::size_t OperandCount = 0;
+};
+
+constexpr std::array LinearFormats{
+ LinearFormat{"none", 0},
+ LinearFormat{"rd-imm", 2},
+ LinearFormat{"rd-rs1-imm", 3},
+ LinearFormat{"rd-rs1-rs2", 3},
+ LinearFormat{"rs1-rs2-target", 3},
+ LinearFormat{"rd-target", 2},
+ LinearFormat{"imm-imm", 2},
+ LinearFormat{"rd-csr-rs1", 3},
+ LinearFormat{"rd-csr-imm", 3},
+ LinearFormat{"frd-frs1-frs2-rm", 4},
+ LinearFormat{"frd-frs1-frs2-frs3-rm", 5},
+ LinearFormat{"frd-frs1-rm", 3},
+ LinearFormat{"frd-frs1-frs2", 3},
+ LinearFormat{"rd-frs1-rm", 3},
+ LinearFormat{"rd-frs1", 2},
+ LinearFormat{"rd-frs1-frs2", 3},
+ LinearFormat{"frd-rs1-rm", 3},
+ LinearFormat{"frd-rs1", 2},
+ LinearFormat{"target", 1},
+ LinearFormat{"rs1-target", 2},
+ LinearFormat{"rs1", 1},
+ LinearFormat{"rd-rs2", 2},
+};
+
+[[nodiscard]] const AssemblyFormatInfo *assemblyFormat(AssemblyFormatID ID) noexcept {
+ const auto Formats = assemblyFormats();
+ const auto Position = std::ranges::find(Formats, ID, &AssemblyFormatInfo::ID);
+ return Position == Formats.end() ? nullptr : &*Position;
+}
+
+[[nodiscard]] std::optional<std::size_t> linearOperandCount(std::string_view Name) noexcept {
+ const auto *const Position = std::ranges::find(LinearFormats, Name, &LinearFormat::Name);
+ return Position == LinearFormats.end() ? std::nullopt : std::optional(Position->OperandCount);
+}
+
+[[nodiscard]] Expected<void> requireOperandCount(const mc::MCInst &Instruction,
+ std::size_t ExpectedCount) {
+ if (Instruction.operands().size() != ExpectedCount) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V assembly format has an unexpected operand count");
+ }
+ return {};
+}
+
+[[nodiscard]] std::string hexValue(std::uint64_t Value) {
+ std::array<char, 16> Digits{};
+ const auto [End, Error] = std::to_chars(Digits.data(), Digits.data() + Digits.size(), Value, 16);
+ if (Error != std::errc{})
+ return {};
+ return "0x" + std::string(Digits.data(), End);
+}
+
+[[nodiscard]] Expected<std::string> formatOperand(const mc::MCOperand &Operand,
+ const TargetDescription &Target,
+ const mc::MCInstPrinter &Printer) {
+ if (const mc::MCRegister *Register = Operand.registerValue(); Register != nullptr) {
+ auto Physical = PhysicalRegister::create(Register->value());
+ if (!Physical)
+ return std::unexpected(Physical.error());
+ const auto Name = Target.registers().physicalRegisterMIRName(*Physical);
+ if (!Name) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V MC instruction references an unknown register");
+ }
+ return std::string(*Name);
+ }
+ if (const std::int64_t *Immediate = Operand.immediateValue(); Immediate != nullptr)
+ return std::to_string(*Immediate);
+ if (const std::uint32_t *Bits = Operand.float32BitsValue(); Bits != nullptr)
+ return hexValue(*Bits);
+ if (const std::uint64_t *Bits = Operand.float64BitsValue(); Bits != nullptr)
+ return hexValue(*Bits);
+ if (const mc::MCExprRef *Expression = Operand.expressionValue(); Expression != nullptr) {
+ if (!*Expression)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V MC instruction has a null expression operand");
+ return mc::printMCExpression(**Expression, Printer);
+ }
+ return makeUnexpected(ErrorCode::InternalError, "RISC-V MC operand storage is invalid");
+}
+
+[[nodiscard]] Expected<std::string> formatRegisterOperand(const mc::MCInst &Instruction,
+ std::size_t Index,
+ const TargetDescription &Target,
+ const mc::MCInstPrinter &Printer) {
+ if (Index >= Instruction.operands().size() ||
+ Instruction.operands()[Index].registerValue() == nullptr) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V assembly format requires a register operand");
+ }
+ return formatOperand(Instruction.operands()[Index], Target, Printer);
+}
+
+[[nodiscard]] Expected<std::string> formatAnyOperand(const mc::MCInst &Instruction,
+ std::size_t Index,
+ const TargetDescription &Target,
+ const mc::MCInstPrinter &Printer) {
+ if (Index >= Instruction.operands().size()) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V assembly format references an absent operand");
+ }
+ return formatOperand(Instruction.operands()[Index], Target, Printer);
+}
+
+[[nodiscard]] Expected<std::string> formatAddress(const mc::MCInst &Instruction,
+ std::size_t OffsetIndex, std::size_t BaseIndex,
+ const TargetDescription &Target,
+ const mc::MCInstPrinter &Printer) {
+ auto Offset = formatAnyOperand(Instruction, OffsetIndex, Target, Printer);
+ if (!Offset)
+ return std::unexpected(Offset.error());
+ auto Base = formatRegisterOperand(Instruction, BaseIndex, Target, Printer);
+ if (!Base)
+ return std::unexpected(Base.error());
+ return *Offset + "(" + *Base + ")";
+}
+
+[[nodiscard]] Expected<std::string> joinOperands(const mc::MCInst &Instruction, std::size_t Count,
+ const TargetDescription &Target,
+ const mc::MCInstPrinter &Printer) {
+ auto CountResult = requireOperandCount(Instruction, Count);
+ if (!CountResult)
+ return std::unexpected(CountResult.error());
+ std::string Result;
+ for (std::size_t Index = 0; Index < Count; ++Index) {
+ auto Operand = formatAnyOperand(Instruction, Index, Target, Printer);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ if (!Result.empty())
+ Result += ", ";
+ Result += *Operand;
+ }
+ return Result;
+}
+
+} // namespace
+
+Expected<std::string> RISCVInstPrinter::printInstruction(const mc::MCInst &Instruction) const {
+ const AssemblyInstructionInfo *Assembly =
+ assemblyInstruction(Opcode::fromValue(Instruction.opcode().value()));
+ if (Assembly == nullptr || Assembly->Mnemonic.empty()) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V instruction has no generated assembly spelling");
+ }
+ const AssemblyFormatInfo *Format = assemblyFormat(Assembly->Format);
+ if (Format == nullptr) {
+ return makeUnexpected(ErrorCode::InternalError,
+ "RISC-V instruction references an unknown assembly format");
+ }
+
+ std::string Operands;
+ if (const auto LinearCount = linearOperandCount(Format->Name)) {
+ auto Joined = joinOperands(Instruction, *LinearCount, *Target, *this);
+ if (!Joined)
+ return std::unexpected(Joined.error());
+ Operands = std::move(*Joined);
+ } else if (Format->Name == "rd-offset-rs1" || Format->Name == "frd-offset-rs1" ||
+ Format->Name == "rs2-offset-rs1" || Format->Name == "frs2-offset-rs1") {
+ auto Count = requireOperandCount(Instruction, 3);
+ if (!Count)
+ return std::unexpected(Count.error());
+ auto First = formatRegisterOperand(Instruction, 0, *Target, *this);
+ if (!First)
+ return std::unexpected(First.error());
+ auto Address = formatAddress(Instruction, 2, 1, *Target, *this);
+ if (!Address)
+ return std::unexpected(Address.error());
+ Operands = *First + ", " + *Address;
+ } else if (Format->Name == "rd-paren-rs1") {
+ auto Count = requireOperandCount(Instruction, 2);
+ if (!Count)
+ return std::unexpected(Count.error());
+ auto Destination = formatRegisterOperand(Instruction, 0, *Target, *this);
+ if (!Destination)
+ return std::unexpected(Destination.error());
+ auto Base = formatRegisterOperand(Instruction, 1, *Target, *this);
+ if (!Base)
+ return std::unexpected(Base.error());
+ Operands = *Destination + ", (" + *Base + ")";
+ } else if (Format->Name == "rd-rs2-paren-rs1") {
+ auto Count = requireOperandCount(Instruction, 3);
+ if (!Count)
+ return std::unexpected(Count.error());
+ auto Destination = formatRegisterOperand(Instruction, 0, *Target, *this);
+ if (!Destination)
+ return std::unexpected(Destination.error());
+ auto Value = formatRegisterOperand(Instruction, 1, *Target, *this);
+ if (!Value)
+ return std::unexpected(Value.error());
+ auto Base = formatRegisterOperand(Instruction, 2, *Target, *this);
+ if (!Base)
+ return std::unexpected(Base.error());
+ Operands = *Destination + ", " + *Value + ", (" + *Base + ")";
+ } else if (Format->Name == "imm-sp") {
+ auto Count = requireOperandCount(Instruction, 1);
+ if (!Count)
+ return std::unexpected(Count.error());
+ auto Immediate = formatAnyOperand(Instruction, 0, *Target, *this);
+ if (!Immediate)
+ return std::unexpected(Immediate.error());
+ Operands = "sp, " + *Immediate;
+ } else if (Format->Name == "rd-imm-sp") {
+ auto Count = requireOperandCount(Instruction, 2);
+ if (!Count)
+ return std::unexpected(Count.error());
+ auto Destination = formatRegisterOperand(Instruction, 0, *Target, *this);
+ if (!Destination)
+ return std::unexpected(Destination.error());
+ auto Immediate = formatAnyOperand(Instruction, 1, *Target, *this);
+ if (!Immediate)
+ return std::unexpected(Immediate.error());
+ Operands = *Destination + ", sp, " + *Immediate;
+ } else if (Format->Name == "rd-offset-sp" || Format->Name == "frd-offset-sp" ||
+ Format->Name == "rs2-offset-sp" || Format->Name == "frs2-offset-sp") {
+ auto Count = requireOperandCount(Instruction, 2);
+ if (!Count)
+ return std::unexpected(Count.error());
+ auto First = formatRegisterOperand(Instruction, 0, *Target, *this);
+ if (!First)
+ return std::unexpected(First.error());
+ auto Offset = formatAnyOperand(Instruction, 1, *Target, *this);
+ if (!Offset)
+ return std::unexpected(Offset.error());
+ Operands = *First + ", " + *Offset + "(sp)";
+ } else {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V assembly printer does not support generated format '" +
+ std::string(Format->Name) + "'");
+ }
+
+ std::string Result(Assembly->Mnemonic);
+ if (!Operands.empty())
+ Result += " " + Operands;
+ return Result;
+}
+
+Expected<std::string> RISCVInstPrinter::printTargetExpression(std::uint32_t Kind,
+ std::string_view Operand) const {
+ switch (static_cast<RISCVMCExprKind>(Kind)) {
+ case RISCVMCExprKind::CallPLT:
+ return std::string(Operand);
+ case RISCVMCExprKind::PCRelativeHigh:
+ return "%pcrel_hi(" + std::string(Operand) + ")";
+ case RISCVMCExprKind::PCRelativeLow:
+ return "%pcrel_lo(" + std::string(Operand) + ")";
+ }
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V MC expression has an unknown target kind");
+}
+
+} // namespace rcc::riscv
diff --git a/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp b/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp
new file mode 100644
index 0000000..4e8d5ec
--- /dev/null
+++ b/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp
@@ -0,0 +1,1158 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/Target/RISCV/RISCVMCTargetLowering.h"
+
+#include "rcc/CodeGen/GenericConstant.h"
+#include "rcc/CodeGen/GenericOpcodes.h"
+#include "rcc/CodeGen/GlobalObject.h"
+#include "rcc/CodeGen/InstructionDescriptor.h"
+#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineConstantPool.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineInstr.h"
+#include "rcc/CodeGen/MachineJumpTableInfo.h"
+#include "rcc/CodeGen/MachineMCContext.h"
+#include "rcc/CodeGen/MachineOperand.h"
+#include "rcc/CodeGen/Register.h"
+#include "rcc/CodeGen/TargetConstant.h"
+#include "rcc/MC/MCContext.h"
+#include "rcc/MC/MCExpr.h"
+#include "rcc/MC/MCInst.h"
+#include "rcc/MC/MCSection.h"
+#include "rcc/MC/MCStreamer.h"
+#include "rcc/MC/MCSymbol.h"
+#include "rcc/Target/RISCV/RISCVTarget.h"
+
+#include <algorithm>
+#include <bit>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <optional>
+#include <ranges>
+#include <span>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace rcc::riscv {
+namespace {
+
+constexpr std::uint32_t CallTargetFlag = 1;
+constexpr std::uint32_t PCRelativeHighTargetFlag = 2;
+constexpr std::uint32_t PCRelativeLowTargetFlag = 4;
+constexpr std::uint32_t CopyF32TargetFlag = 8;
+constexpr std::uint32_t CopyF64TargetFlag = 16;
+constexpr std::uint32_t KnownTargetFlags = CallTargetFlag | PCRelativeHighTargetFlag |
+ PCRelativeLowTargetFlag | CopyF32TargetFlag |
+ CopyF64TargetFlag;
+
+struct TypeLayout final {
+ std::uint64_t Size = 0;
+ std::uint64_t Alignment = 1;
+};
+
+[[nodiscard]] Expected<std::uint64_t> checkedAdd(std::uint64_t Left, std::uint64_t Right) {
+ std::uint64_t Result = 0;
+ if (__builtin_add_overflow(Left, Right, &Result))
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "constant layout size overflows");
+ return Result;
+}
+
+[[nodiscard]] Expected<std::uint64_t> checkedMultiply(std::uint64_t Left, std::uint64_t Right) {
+ std::uint64_t Result = 0;
+ if (__builtin_mul_overflow(Left, Right, &Result))
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "constant layout size overflows");
+ return Result;
+}
+
+[[nodiscard]] Expected<std::uint64_t> alignTo(std::uint64_t Value, std::uint64_t Alignment) {
+ if (Alignment == 0 || (Alignment & (Alignment - 1U)) != 0) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "constant layout requires power-of-two alignment");
+ }
+ auto Adjusted = checkedAdd(Value, Alignment - 1U);
+ if (!Adjusted)
+ return std::unexpected(Adjusted.error());
+ return *Adjusted & ~(Alignment - 1U);
+}
+
+[[nodiscard]] std::uint64_t naturalAlignment(std::uint64_t Size) noexcept {
+ if (Size <= 1)
+ return 1;
+ std::uint64_t Result = 1;
+ while (Result < Size && Result < 16)
+ Result <<= 1U;
+ return Result;
+}
+
+[[nodiscard]] const GenericConstantType &resolvedType(const GenericConstantType &Type) noexcept {
+ const GenericConstantType *Result = &Type;
+ std::size_t Depth = 0;
+ while (Result->Kind == GenericConstantTypeKind::Named && Result->Elements.size() == 1 &&
+ Depth++ < 1024) {
+ Result = &Result->Elements.front();
+ }
+ return *Result;
+}
+
+[[nodiscard]] Expected<TypeLayout> layoutType(const GenericConstantType &InputType,
+ std::size_t Depth = 0) {
+ if (Depth > 1024) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "constant layout exceeds its nesting limit");
+ }
+ const GenericConstantType &Type = resolvedType(InputType);
+ switch (Type.Kind) {
+ case GenericConstantTypeKind::Integer: {
+ const std::uint64_t Size = (static_cast<std::uint64_t>(Type.BitWidth) + 7U) / 8U;
+ return TypeLayout{.Size = Size, .Alignment = naturalAlignment(Size)};
+ }
+ case GenericConstantTypeKind::Byte:
+ return TypeLayout{.Size = 1, .Alignment = 1};
+ case GenericConstantTypeKind::Floating: {
+ const auto BitWidth = getFloatSemanticsBitWidth(Type.Semantics);
+ if (!BitWidth) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "floating constant has unknown semantics");
+ }
+ const std::uint64_t Size = (static_cast<std::uint64_t>(*BitWidth) + 7U) / 8U;
+ return TypeLayout{.Size = Size, .Alignment = naturalAlignment(Size)};
+ }
+ case GenericConstantTypeKind::Pointer:
+ return TypeLayout{.Size = 8, .Alignment = 8};
+ case GenericConstantTypeKind::Array:
+ case GenericConstantTypeKind::Vector: {
+ if (Type.Elements.size() != 1 ||
+ (Type.Kind == GenericConstantTypeKind::Vector && Type.Scalable)) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC assembly cannot lay out this sequential constant type");
+ }
+ auto Element = layoutType(Type.Elements.front(), Depth + 1);
+ if (!Element)
+ return std::unexpected(Element.error());
+ auto Stride = alignTo(Element->Size, Element->Alignment);
+ if (!Stride)
+ return std::unexpected(Stride.error());
+ auto Size = checkedMultiply(*Stride, Type.ElementCount);
+ if (!Size)
+ return std::unexpected(Size.error());
+ std::uint64_t Alignment = Element->Alignment;
+ if (Type.Kind == GenericConstantTypeKind::Vector)
+ Alignment = naturalAlignment(*Size);
+ return TypeLayout{.Size = *Size, .Alignment = Alignment};
+ }
+ case GenericConstantTypeKind::Struct: {
+ std::uint64_t Offset = 0;
+ std::uint64_t StructAlignment = 1;
+ for (const GenericConstantType &ElementType : Type.Elements) {
+ auto Element = layoutType(ElementType, Depth + 1);
+ if (!Element)
+ return std::unexpected(Element.error());
+ const std::uint64_t ElementAlignment = Type.Packed ? 1 : Element->Alignment;
+ auto Aligned = alignTo(Offset, ElementAlignment);
+ if (!Aligned)
+ return std::unexpected(Aligned.error());
+ auto End = checkedAdd(*Aligned, Element->Size);
+ if (!End)
+ return std::unexpected(End.error());
+ Offset = *End;
+ StructAlignment = std::max(StructAlignment, ElementAlignment);
+ }
+ auto Size = alignTo(Offset, StructAlignment);
+ if (!Size)
+ return std::unexpected(Size.error());
+ return TypeLayout{.Size = *Size, .Alignment = StructAlignment};
+ }
+ case GenericConstantTypeKind::Named:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC assembly cannot lay out an unresolved named constant type");
+ case GenericConstantTypeKind::Invalid:
+ case GenericConstantTypeKind::Void:
+ case GenericConstantTypeKind::Label:
+ case GenericConstantTypeKind::Token:
+ case GenericConstantTypeKind::Metadata:
+ case GenericConstantTypeKind::Function:
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "constant has a non-storage type");
+ }
+ return makeUnexpected(ErrorCode::InternalError, "generic constant type kind is invalid");
+}
+
+[[nodiscard]] Expected<mc::MCExprRef> addOffset(mc::MCExprRef Expression, std::int64_t Offset) {
+ if (Offset == 0)
+ return Expression;
+ auto Result = mc::MCExpr::binary(mc::MCBinaryOperator::Add, std::move(Expression),
+ mc::MCExpr::constant(Offset));
+ if (!Result)
+ return std::unexpected(Result.error());
+ return *Result;
+}
+
+[[nodiscard]] Expected<mc::MCExprRef> applyTargetFlags(mc::MCExprRef Expression,
+ std::uint32_t Flags) {
+ if ((Flags & ~KnownTargetFlags) != 0 || (Flags & (CopyF32TargetFlag | CopyF64TargetFlag)) != 0) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V operand has target flags that cannot survive MC lowering");
+ }
+ if (Flags == 0)
+ return Expression;
+
+ RISCVMCExprKind Kind;
+ switch (Flags) {
+ case CallTargetFlag:
+ Kind = RISCVMCExprKind::CallPLT;
+ break;
+ case PCRelativeHighTargetFlag:
+ Kind = RISCVMCExprKind::PCRelativeHigh;
+ break;
+ case PCRelativeLowTargetFlag:
+ Kind = RISCVMCExprKind::PCRelativeLow;
+ break;
+ default:
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V operand combines incompatible direct target flags");
+ }
+ auto TargetExpression =
+ mc::MCExpr::target(static_cast<std::uint32_t>(Kind), std::move(Expression));
+ if (!TargetExpression)
+ return std::unexpected(TargetExpression.error());
+ return *TargetExpression;
+}
+
+[[nodiscard]] Expected<mc::MCOperand> symbolOperand(mc::MCSymbolRef Symbol, std::int64_t Offset,
+ std::uint32_t TargetFlags) {
+ auto Expression = mc::MCExpr::symbol(std::move(Symbol));
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ auto WithOffset = addOffset(std::move(*Expression), Offset);
+ if (!WithOffset)
+ return std::unexpected(WithOffset.error());
+ auto TargetExpression = applyTargetFlags(std::move(*WithOffset), TargetFlags);
+ if (!TargetExpression)
+ return std::unexpected(TargetExpression.error());
+ return mc::MCOperand::expression(std::move(*TargetExpression));
+}
+
+[[nodiscard]] Expected<std::int64_t> lowerIntegerImmediate(const IntegerImmediate &Value) {
+ if (Value.bitWidth() > 64 || Value.words().size() != 1) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V instruction has an integer immediate wider than 64 bits");
+ }
+ const std::uint64_t Raw = Value.words().front();
+ if (Value.bitWidth() == 64)
+ return std::bit_cast<std::int64_t>(Raw);
+ if (!Value.isNegative())
+ return static_cast<std::int64_t>(Raw);
+ const std::uint64_t Mask = (std::uint64_t{1} << Value.bitWidth()) - 1U;
+ return std::bit_cast<std::int64_t>(Raw | ~Mask);
+}
+
+[[nodiscard]] Expected<mc::MCOperand> lowerOperand(const MachineOperand &Operand,
+ const MachineFunction &Function,
+ MachineMCContext &Context) {
+ if (const auto *RegisterValue = Operand.getIf<Register>()) {
+ const auto Physical = RegisterValue->physicalRegister();
+ if (!Physical || Operand.subReg().value() != 0) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V MC lowering requires a resolved physical register");
+ }
+ if (Operand.targetFlags() != 0) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V register operand retains target flags at MC lowering");
+ }
+ return mc::MCOperand::registerOperand(mc::MCRegister::fromValue(Physical->number()));
+ }
+ if (const auto *Immediate = Operand.getIf<std::int64_t>()) {
+ if (Operand.targetFlags() != 0) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V immediate operand has symbol-only target flags");
+ }
+ return mc::MCOperand::immediate(*Immediate);
+ }
+ if (const auto *Immediate = Operand.getIf<IntegerImmediateOperand>()) {
+ if (Operand.targetFlags() != 0) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V integer immediate has symbol-only target flags");
+ }
+ auto Lowered = lowerIntegerImmediate(Immediate->Value);
+ if (!Lowered)
+ return std::unexpected(Lowered.error());
+ return mc::MCOperand::immediate(*Lowered);
+ }
+ if (const auto *Immediate = Operand.getIf<FloatingImmediateOperand>()) {
+ if (Operand.targetFlags() != 0 || Immediate->Bits.words().size() != 1) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V floating immediate cannot be represented in MCInst");
+ }
+ if (Immediate->Bits.bitWidth() == 32)
+ return mc::MCOperand::float32Bits(static_cast<std::uint32_t>(Immediate->Bits.words()[0]));
+ if (Immediate->Bits.bitWidth() == 64)
+ return mc::MCOperand::float64Bits(Immediate->Bits.words()[0]);
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RISC-V MC lowering supports only 32-bit and 64-bit FP immediates");
+ }
+ if (const auto *Block = Operand.getIf<BasicBlockOperand>()) {
+ if (Block->Block == nullptr)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "RISC-V block operand is null");
+ const mc::MCSymbolRef Symbol = Context.blockSymbol(*Block->Block);
+ if (!Symbol)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V block operand has no MC symbol");
+ return symbolOperand(Symbol, 0, Operand.targetFlags());
+ }
+ if (const auto *Pool = Operand.getIf<ConstantPoolOperand>()) {
+ const mc::MCSymbolRef Symbol = Context.constantPoolSymbol(Function, Pool->Index);
+ if (!Symbol)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V constant-pool operand has no MC symbol");
+ return symbolOperand(Symbol, Pool->Offset, Operand.targetFlags());
+ }
+ if (const auto *Table = Operand.getIf<JumpTableOperand>()) {
+ const mc::MCSymbolRef Symbol = Context.jumpTableSymbol(Function, Table->Index);
+ if (!Symbol)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V jump-table operand has no MC symbol");
+ return symbolOperand(Symbol, 0, Operand.targetFlags());
+ }
+ if (const auto *Global = Operand.getIf<GlobalSymbolOperand>()) {
+ auto Symbol = Context.symbol(Global->Name, mc::MCSymbolKind::Object);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ return symbolOperand(std::move(*Symbol), Global->Offset, Operand.targetFlags());
+ }
+ if (const auto *External = Operand.getIf<ExternalSymbolOperand>()) {
+ auto Symbol = Context.symbol(External->Name);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ return symbolOperand(std::move(*Symbol), External->Offset, Operand.targetFlags());
+ }
+ if (const auto *Address = Operand.getIf<BlockAddressOperand>()) {
+ auto Symbol = Context.blockAddressSymbol(Address->FunctionName, Address->BlockName);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ return symbolOperand(std::move(*Symbol), Address->Offset, Operand.targetFlags());
+ }
+ if (const auto *SymbolValue = Operand.getIf<MCSymbolOperand>()) {
+ auto Symbol = Context.symbol(SymbolValue->Name);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ return symbolOperand(std::move(*Symbol), SymbolValue->Offset, Operand.targetFlags());
+ }
+ if (Operand.kind() == MachineOperand::Kind::FrameIndex) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V MC lowering encountered an unresolved frame index");
+ }
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V MC lowering encountered a non-encoding operand");
+}
+
+[[nodiscard]] const AssemblyFormatInfo *assemblyFormat(AssemblyFormatID ID) noexcept {
+ const auto Formats = assemblyFormats();
+ const auto Position = std::ranges::find(Formats, ID, &AssemblyFormatInfo::ID);
+ return Position == Formats.end() ? nullptr : &*Position;
+}
+
+[[nodiscard]] std::optional<std::size_t> formatOperandCount(std::string_view Name) noexcept {
+ if (Name == "none")
+ return 0;
+ if (Name == "rs1" || Name == "target" || Name == "imm-sp")
+ return 1;
+ if (Name == "rd-imm" || Name == "rd-target" || Name == "rd-frs1" || Name == "frd-rs1" ||
+ Name == "rs1-target" || Name == "rd-rs2" || Name == "rd-paren-rs1" || Name == "rd-imm-sp" ||
+ Name == "rd-offset-sp" || Name == "rs2-offset-sp" || Name == "frd-offset-sp" ||
+ Name == "frs2-offset-sp") {
+ return 2;
+ }
+ if (Name == "rd-rs1-imm" || Name == "rd-rs1-rs2" || Name == "rd-offset-rs1" ||
+ Name == "rs2-offset-rs1" || Name == "rs1-rs2-target" || Name == "imm-imm" ||
+ Name == "rd-csr-rs1" || Name == "rd-csr-imm" || Name == "rd-rs2-paren-rs1" ||
+ Name == "frd-offset-rs1" || Name == "frs2-offset-rs1" || Name == "frd-frs1-rm" ||
+ Name == "frd-frs1-frs2" || Name == "rd-frs1-rm" || Name == "rd-frs1-frs2" ||
+ Name == "frd-rs1-rm") {
+ return 3;
+ }
+ if (Name == "frd-frs1-frs2-rm")
+ return 4;
+ if (Name == "frd-frs1-frs2-frs3-rm")
+ return 5;
+ return std::nullopt;
+}
+
+[[nodiscard]] std::vector<std::uint8_t> bytesFromWords(std::span<const std::uint64_t> Words,
+ std::uint64_t Size) {
+ std::vector<std::uint8_t> Bytes(static_cast<std::size_t>(Size), 0);
+ for (std::uint64_t ByteIndex = 0; ByteIndex < Size; ++ByteIndex) {
+ const std::size_t WordIndex = static_cast<std::size_t>(ByteIndex / 8U);
+ if (WordIndex >= Words.size())
+ break;
+ const unsigned Shift = static_cast<unsigned>((ByteIndex % 8U) * 8U);
+ Bytes[static_cast<std::size_t>(ByteIndex)] =
+ static_cast<std::uint8_t>((Words[WordIndex] >> Shift) & 0xffU);
+ }
+ return Bytes;
+}
+
+[[nodiscard]] Expected<mc::MCExprRef> lowerConstantExpression(const GenericConstantData &Constant,
+ MachineMCContext &Context,
+ std::size_t Depth = 0) {
+ if (Depth > 1024) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "constant expression exceeds its nesting limit");
+ }
+ switch (Constant.Kind) {
+ case GenericConstantKind::Integer: {
+ if (!Constant.IntegerValue || Constant.IntegerValue->bitWidth() > 64 ||
+ Constant.IntegerValue->words().size() != 1) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC MC expression requires an integer no wider than 64 bits");
+ }
+ const std::uint64_t Raw = Constant.IntegerValue->words().front();
+ const std::int64_t Value = Constant.IntegerValue->bitWidth() == 64
+ ? std::bit_cast<std::int64_t>(Raw)
+ : static_cast<std::int64_t>(Raw);
+ return mc::MCExpr::constant(Value);
+ }
+ case GenericConstantKind::NullPointer:
+ case GenericConstantKind::ZeroInitializer:
+ case GenericConstantKind::Undef:
+ case GenericConstantKind::Poison:
+ return mc::MCExpr::constant(0);
+ case GenericConstantKind::GlobalAddress:
+ case GenericConstantKind::DSOLocalEquivalent:
+ case GenericConstantKind::NoCFI: {
+ auto Symbol = Context.symbol(Constant.Symbol, mc::MCSymbolKind::Object);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ return mc::MCExpr::symbol(std::move(*Symbol));
+ }
+ case GenericConstantKind::BlockAddress: {
+ auto Symbol = Context.blockAddressSymbol(Constant.Symbol, Constant.Block);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ return mc::MCExpr::symbol(std::move(*Symbol));
+ }
+ case GenericConstantKind::Expression: {
+ if (Constant.Elements.empty()) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "generic constant expression has no operands");
+ }
+ auto Left = lowerConstantExpression(Constant.Elements.front(), Context, Depth + 1);
+ if (!Left)
+ return std::unexpected(Left.error());
+ switch (Constant.ExpressionOpcode) {
+ case GenericConstantExpressionOpcode::Trunc:
+ case GenericConstantExpressionOpcode::BitCast:
+ case GenericConstantExpressionOpcode::AddressSpaceCast:
+ case GenericConstantExpressionOpcode::IntToPtr:
+ case GenericConstantExpressionOpcode::PtrToAddr:
+ case GenericConstantExpressionOpcode::PtrToInt:
+ return *Left;
+ case GenericConstantExpressionOpcode::Add:
+ case GenericConstantExpressionOpcode::Sub:
+ case GenericConstantExpressionOpcode::Xor: {
+ if (Constant.Elements.size() != 2) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "binary constant expression has the wrong operand count");
+ }
+ auto Right = lowerConstantExpression(Constant.Elements[1], Context, Depth + 1);
+ if (!Right)
+ return std::unexpected(Right.error());
+ const mc::MCBinaryOperator Operator =
+ Constant.ExpressionOpcode == GenericConstantExpressionOpcode::Add
+ ? mc::MCBinaryOperator::Add
+ : Constant.ExpressionOpcode == GenericConstantExpressionOpcode::Sub
+ ? mc::MCBinaryOperator::Subtract
+ : mc::MCBinaryOperator::BitwiseXor;
+ return mc::MCExpr::binary(Operator, std::move(*Left), std::move(*Right));
+ }
+ case GenericConstantExpressionOpcode::Invalid:
+ case GenericConstantExpressionOpcode::GetElementPtr:
+ case GenericConstantExpressionOpcode::ShuffleVector:
+ case GenericConstantExpressionOpcode::InsertElement:
+ case GenericConstantExpressionOpcode::ExtractElement:
+ case GenericConstantExpressionOpcode::Splat:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC assembly cannot lower this generic constant expression");
+ }
+ }
+ case GenericConstantKind::Invalid:
+ case GenericConstantKind::Floating:
+ case GenericConstantKind::Aggregate:
+ case GenericConstantKind::ByteString:
+ case GenericConstantKind::PointerAuth:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "generic constant cannot be represented as an MC expression");
+ }
+ return makeUnexpected(ErrorCode::InternalError, "generic constant kind is invalid");
+}
+
+[[nodiscard]] Expected<void> emitPadding(std::uint64_t Size, mc::MCStreamer &Streamer) {
+ return Size == 0 ? Expected<void>{} : Streamer.emitZeroFill(Size);
+}
+
+[[nodiscard]] Expected<void> emitGenericConstantImpl(const GenericConstantData &Constant,
+ const TypeLayout &Layout,
+ MachineMCContext &Context,
+ mc::MCStreamer &Streamer,
+ std::size_t Depth = 0) {
+ if (Depth > 1024) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "generic constant exceeds its emission nesting limit");
+ }
+ switch (Constant.Kind) {
+ case GenericConstantKind::Integer: {
+ if (!Constant.IntegerValue)
+ return makeUnexpected(ErrorCode::InternalError, "integer constant has no value");
+ const auto Bytes = bytesFromWords(Constant.IntegerValue->words(), Layout.Size);
+ return Streamer.emitBytes(Bytes);
+ }
+ case GenericConstantKind::Floating: {
+ if (!Constant.FloatingValue)
+ return makeUnexpected(ErrorCode::InternalError, "floating constant has no value");
+ const auto Bytes = bytesFromWords(Constant.FloatingValue->words(), Layout.Size);
+ return Streamer.emitBytes(Bytes);
+ }
+ case GenericConstantKind::ByteString: {
+ if (Constant.ByteValue.size() != Layout.Size) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "byte-string constant size does not match its RV64GC layout");
+ }
+ std::vector<std::uint8_t> Bytes;
+ Bytes.reserve(Constant.ByteValue.size());
+ for (const std::byte Byte : Constant.ByteValue)
+ Bytes.push_back(std::to_integer<std::uint8_t>(Byte));
+ return Streamer.emitBytes(Bytes);
+ }
+ case GenericConstantKind::NullPointer:
+ case GenericConstantKind::ZeroInitializer:
+ case GenericConstantKind::Undef:
+ case GenericConstantKind::Poison:
+ return Streamer.emitZeroFill(Layout.Size);
+ case GenericConstantKind::Aggregate: {
+ const GenericConstantType &Type = resolvedType(Constant.Type);
+ std::uint64_t Offset = 0;
+ for (std::size_t Index = 0; Index < Constant.Elements.size(); ++Index) {
+ auto ElementLayout = layoutType(Constant.Elements[Index].Type, Depth + 1);
+ if (!ElementLayout)
+ return std::unexpected(ElementLayout.error());
+ const std::uint64_t Alignment = Type.Kind == GenericConstantTypeKind::Struct && Type.Packed
+ ? 1
+ : ElementLayout->Alignment;
+ auto ElementOffset = alignTo(Offset, Alignment);
+ if (!ElementOffset)
+ return std::unexpected(ElementOffset.error());
+ auto Padding = emitPadding(*ElementOffset - Offset, Streamer);
+ if (!Padding)
+ return std::unexpected(Padding.error());
+ auto Element = emitGenericConstantImpl(Constant.Elements[Index], *ElementLayout, Context,
+ Streamer, Depth + 1);
+ if (!Element)
+ return std::unexpected(Element.error());
+ auto End = checkedAdd(*ElementOffset, ElementLayout->Size);
+ if (!End)
+ return std::unexpected(End.error());
+ Offset = *End;
+ if (Type.Kind == GenericConstantTypeKind::Array ||
+ Type.Kind == GenericConstantTypeKind::Vector) {
+ auto StrideEnd = alignTo(Offset, ElementLayout->Alignment);
+ if (!StrideEnd)
+ return std::unexpected(StrideEnd.error());
+ auto ElementPadding = emitPadding(*StrideEnd - Offset, Streamer);
+ if (!ElementPadding)
+ return std::unexpected(ElementPadding.error());
+ Offset = *StrideEnd;
+ }
+ }
+ if (Offset > Layout.Size) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "aggregate constant exceeds its RV64GC layout");
+ }
+ return emitPadding(Layout.Size - Offset, Streamer);
+ }
+ case GenericConstantKind::GlobalAddress:
+ case GenericConstantKind::BlockAddress:
+ case GenericConstantKind::DSOLocalEquivalent:
+ case GenericConstantKind::NoCFI:
+ case GenericConstantKind::Expression: {
+ if (Layout.Size != 1 && Layout.Size != 2 && Layout.Size != 4 && Layout.Size != 8) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "symbolic RV64GC constant has an unsupported storage size");
+ }
+ auto Expression = lowerConstantExpression(Constant, Context);
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ return Streamer.emitValue(std::move(*Expression), static_cast<std::uint8_t>(Layout.Size));
+ }
+ case GenericConstantKind::PointerAuth:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC assembly does not support pointer-auth constants");
+ case GenericConstantKind::Invalid:
+ return makeUnexpected(ErrorCode::InternalError, "generic constant kind is invalid");
+ }
+ return makeUnexpected(ErrorCode::InternalError, "generic constant kind is invalid");
+}
+
+[[nodiscard]] Expected<void> emitRepeatedByte(std::uint8_t Byte, std::uint64_t Size,
+ mc::MCStreamer &Streamer) {
+ if (Size == 0)
+ return {};
+ if (Byte == 0)
+ return Streamer.emitZeroFill(Size);
+ constexpr std::uint64_t ChunkSize = 4096;
+ const std::size_t InitialSize = static_cast<std::size_t>(std::min(Size, ChunkSize));
+ const std::vector<std::uint8_t> Chunk(InitialSize, Byte);
+ while (Size != 0) {
+ const std::size_t EmittedSize = static_cast<std::size_t>(std::min(Size, ChunkSize));
+ auto Emitted = Streamer.emitBytes(std::span<const std::uint8_t>(Chunk).first(EmittedSize));
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ Size -= static_cast<std::uint64_t>(EmittedSize);
+ }
+ return {};
+}
+
+[[nodiscard]] Expected<void> emitGlobalInitializerImpl(const GlobalInitializer &Initializer,
+ MachineMCContext &Context,
+ mc::MCStreamer &Streamer,
+ std::size_t Depth = 0) {
+ if (Depth > 1024) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "global initializer exceeds its emission nesting limit");
+ }
+ switch (Initializer.kind()) {
+ case GlobalInitializer::Kind::Integer: {
+ const auto *Value = Initializer.getIf<IntegerImmediate>();
+ if (Value == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "integer initializer has no value");
+ const std::uint64_t Size = (static_cast<std::uint64_t>(Value->bitWidth()) + 7U) / 8U;
+ return Streamer.emitBytes(bytesFromWords(Value->words(), Size));
+ }
+ case GlobalInitializer::Kind::Floating: {
+ const auto *Value = Initializer.getIf<FloatingPointBits>();
+ if (Value == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "floating initializer has no value");
+ const std::uint64_t Size = (static_cast<std::uint64_t>(Value->bitWidth()) + 7U) / 8U;
+ return Streamer.emitBytes(bytesFromWords(Value->words(), Size));
+ }
+ case GlobalInitializer::Kind::Bytes: {
+ const auto *Value = Initializer.getIf<std::vector<std::byte>>();
+ if (Value == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "byte initializer has no value");
+ std::vector<std::uint8_t> Bytes;
+ Bytes.reserve(Value->size());
+ for (const std::byte Byte : *Value)
+ Bytes.push_back(std::to_integer<std::uint8_t>(Byte));
+ return Streamer.emitBytes(Bytes);
+ }
+ case GlobalInitializer::Kind::ZeroFill: {
+ const auto *Value = Initializer.getIf<GlobalZeroFill>();
+ return Value == nullptr
+ ? makeUnexpected(ErrorCode::InternalError, "zero-fill initializer has no value")
+ : Streamer.emitZeroFill(Value->Size);
+ }
+ case GlobalInitializer::Kind::Repeated: {
+ const auto *Value = Initializer.getIf<GlobalRepeatedInitializer>();
+ if (Value == nullptr || !Value->Value)
+ return makeUnexpected(ErrorCode::InternalError, "repeated initializer has no value");
+ for (std::uint64_t Index = 0; Index < Value->Count; ++Index) {
+ auto Emitted = emitGlobalInitializerImpl(*Value->Value, Context, Streamer, Depth + 1);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ }
+ return {};
+ }
+ case GlobalInitializer::Kind::Aggregate: {
+ const auto *Value = Initializer.getIf<GlobalAggregateInitializer>();
+ if (Value == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "aggregate initializer has no value");
+ for (const GlobalInitializerRef &Element : Value->Elements) {
+ if (!Element)
+ return makeUnexpected(ErrorCode::InternalError, "aggregate initializer is null");
+ auto Emitted = emitGlobalInitializerImpl(*Element, Context, Streamer, Depth + 1);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ }
+ return {};
+ }
+ case GlobalInitializer::Kind::SymbolAddress: {
+ const auto *Value = Initializer.getIf<GlobalSymbolAddress>();
+ if (Value == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "symbol initializer has no value");
+ auto Symbol = Context.symbol(Value->Symbol, mc::MCSymbolKind::Object);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ auto Expression = mc::MCExpr::symbol(std::move(*Symbol));
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ auto WithAddend = addOffset(std::move(*Expression), Value->Addend);
+ if (!WithAddend)
+ return std::unexpected(WithAddend.error());
+ auto Lowered = applyTargetFlags(std::move(*WithAddend), Value->TargetFlags);
+ if (!Lowered)
+ return std::unexpected(Lowered.error());
+ return Streamer.emitValue(std::move(*Lowered), Value->Size);
+ }
+ case GlobalInitializer::Kind::SymbolDifference: {
+ const auto *Value = Initializer.getIf<GlobalSymbolDifference>();
+ if (Value == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "symbol difference has no value");
+ auto MinuendSymbol = Context.symbol(Value->Minuend);
+ if (!MinuendSymbol)
+ return std::unexpected(MinuendSymbol.error());
+ auto SubtrahendSymbol = Context.symbol(Value->Subtrahend);
+ if (!SubtrahendSymbol)
+ return std::unexpected(SubtrahendSymbol.error());
+ auto Minuend = mc::MCExpr::symbol(std::move(*MinuendSymbol));
+ if (!Minuend)
+ return std::unexpected(Minuend.error());
+ auto Subtrahend = mc::MCExpr::symbol(std::move(*SubtrahendSymbol));
+ if (!Subtrahend)
+ return std::unexpected(Subtrahend.error());
+ auto Difference = mc::MCExpr::binary(mc::MCBinaryOperator::Subtract, std::move(*Minuend),
+ std::move(*Subtrahend));
+ if (!Difference)
+ return std::unexpected(Difference.error());
+ auto WithAddend = addOffset(std::move(*Difference), Value->Addend);
+ if (!WithAddend)
+ return std::unexpected(WithAddend.error());
+ return Streamer.emitValue(std::move(*WithAddend), Value->Size);
+ }
+ case GlobalInitializer::Kind::Target: {
+ const auto *Value = Initializer.getIf<TargetConstant>();
+ if (Value == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "target initializer has no value");
+ auto Emission = Value->emit();
+ if (!Emission)
+ return std::unexpected(Emission.error());
+ if (!Emission->Fixups.empty()) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC target initializer fixups require native object emission");
+ }
+ std::vector<std::uint8_t> Bytes;
+ Bytes.reserve(Emission->Bytes.size());
+ for (const std::byte Byte : Emission->Bytes)
+ Bytes.push_back(std::to_integer<std::uint8_t>(Byte));
+ return Streamer.emitBytes(Bytes);
+ }
+ case GlobalInitializer::Kind::AlignmentPadding: {
+ const auto *Value = Initializer.getIf<GlobalAlignmentPadding>();
+ return Value == nullptr
+ ? makeUnexpected(ErrorCode::InternalError, "padding initializer has no value")
+ : emitRepeatedByte(Value->Fill, Value->Size, Streamer);
+ }
+ case GlobalInitializer::Kind::UndefinedStorage: {
+ const auto *Value = Initializer.getIf<GlobalUndefinedStorage>();
+ return Value == nullptr
+ ? makeUnexpected(ErrorCode::InternalError, "undefined initializer has no value")
+ : Streamer.emitZeroFill(Value->Size);
+ }
+ case GlobalInitializer::Kind::Invalid:
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "global initializer is invalid");
+ }
+ return makeUnexpected(ErrorCode::InternalError, "global initializer kind is invalid");
+}
+
+[[nodiscard]] bool initializerIsZero(const GlobalInitializer &Initializer, std::size_t Depth = 0) {
+ if (Depth > 1024)
+ return false;
+ switch (Initializer.kind()) {
+ case GlobalInitializer::Kind::Integer: {
+ const auto *Value = Initializer.getIf<IntegerImmediate>();
+ return Value != nullptr &&
+ std::ranges::all_of(Value->words(), [](std::uint64_t Word) { return Word == 0; });
+ }
+ case GlobalInitializer::Kind::Floating: {
+ const auto *Value = Initializer.getIf<FloatingPointBits>();
+ return Value != nullptr &&
+ std::ranges::all_of(Value->words(), [](std::uint64_t Word) { return Word == 0; });
+ }
+ case GlobalInitializer::Kind::Bytes: {
+ const auto *Value = Initializer.getIf<std::vector<std::byte>>();
+ return Value != nullptr &&
+ std::ranges::all_of(*Value, [](std::byte Byte) { return Byte == std::byte{0}; });
+ }
+ case GlobalInitializer::Kind::ZeroFill:
+ case GlobalInitializer::Kind::UndefinedStorage:
+ return true;
+ case GlobalInitializer::Kind::Repeated: {
+ const auto *Value = Initializer.getIf<GlobalRepeatedInitializer>();
+ return Value != nullptr && Value->Value && initializerIsZero(*Value->Value, Depth + 1);
+ }
+ case GlobalInitializer::Kind::Aggregate: {
+ const auto *Value = Initializer.getIf<GlobalAggregateInitializer>();
+ return Value != nullptr && std::ranges::all_of(Value->Elements, [Depth](const auto &Element) {
+ return Element && initializerIsZero(*Element, Depth + 1);
+ });
+ }
+ case GlobalInitializer::Kind::AlignmentPadding: {
+ const auto *Value = Initializer.getIf<GlobalAlignmentPadding>();
+ return Value != nullptr && Value->Fill == 0;
+ }
+ case GlobalInitializer::Kind::Target: {
+ const auto *Value = Initializer.getIf<TargetConstant>();
+ if (Value == nullptr)
+ return false;
+ auto Emission = Value->emit();
+ return Emission && Emission->Fixups.empty() &&
+ std::ranges::all_of(Emission->Bytes,
+ [](std::byte Byte) { return Byte == std::byte{0}; });
+ }
+ case GlobalInitializer::Kind::Invalid:
+ case GlobalInitializer::Kind::SymbolAddress:
+ case GlobalInitializer::Kind::SymbolDifference:
+ return false;
+ }
+ return false;
+}
+
+[[nodiscard]] bool initializerHasRelocation(const GlobalInitializer &Initializer,
+ std::size_t Depth = 0) {
+ if (Depth > 1024)
+ return true;
+ switch (Initializer.kind()) {
+ case GlobalInitializer::Kind::SymbolAddress:
+ case GlobalInitializer::Kind::SymbolDifference:
+ return true;
+ case GlobalInitializer::Kind::Repeated: {
+ const auto *Value = Initializer.getIf<GlobalRepeatedInitializer>();
+ return Value == nullptr || !Value->Value || initializerHasRelocation(*Value->Value, Depth + 1);
+ }
+ case GlobalInitializer::Kind::Aggregate: {
+ const auto *Value = Initializer.getIf<GlobalAggregateInitializer>();
+ return Value == nullptr || std::ranges::any_of(Value->Elements, [Depth](const auto &Element) {
+ return !Element || initializerHasRelocation(*Element, Depth + 1);
+ });
+ }
+ case GlobalInitializer::Kind::Target: {
+ const auto *Value = Initializer.getIf<TargetConstant>();
+ if (Value == nullptr)
+ return true;
+ auto Emission = Value->emit();
+ return !Emission || !Emission->Fixups.empty();
+ }
+ case GlobalInitializer::Kind::Invalid:
+ return true;
+ case GlobalInitializer::Kind::Integer:
+ case GlobalInitializer::Kind::Floating:
+ case GlobalInitializer::Kind::Bytes:
+ case GlobalInitializer::Kind::ZeroFill:
+ case GlobalInitializer::Kind::AlignmentPadding:
+ case GlobalInitializer::Kind::UndefinedStorage:
+ return false;
+ }
+ return true;
+}
+
+[[nodiscard]] mc::MCSectionDesc textSectionDescription() {
+ return mc::MCSectionDesc{
+ .Name = ".text",
+ .Kind = mc::MCSectionKind::Text,
+ .Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Executable},
+ .Alignment = 4,
+ .EntrySize = 0,
+ .Group = {},
+ .UniqueID = std::nullopt,
+ };
+}
+
+[[nodiscard]] mc::MCSectionDesc readOnlySectionDescription() {
+ return mc::MCSectionDesc{
+ .Name = ".rodata",
+ .Kind = mc::MCSectionKind::ReadOnly,
+ .Flags = {mc::MCSectionFlag::Alloc},
+ .Alignment = 1,
+ .EntrySize = 0,
+ .Group = {},
+ .UniqueID = std::nullopt,
+ };
+}
+
+} // namespace
+
+Expected<mc::MCInst> RISCVMCTargetLowering::lowerInstruction(const MachineInstr &Instruction,
+ MachineMCContext &Context) const {
+ const InstructionDescriptor *Descriptor = Target->instructionDescriptor(Instruction.opcode());
+ const AssemblyInstructionInfo *Assembly = assemblyInstruction(Instruction.opcode());
+ if (Descriptor == nullptr || Assembly == nullptr || Assembly->Mnemonic.empty()) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V instruction has no MC lowering description");
+ }
+ const AssemblyFormatInfo *Format = assemblyFormat(Assembly->Format);
+ if (Format == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "RISC-V assembly format is missing");
+ const auto RequiredOperands = formatOperandCount(Format->Name);
+ if (!RequiredOperands) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V instruction uses an unsupported assembly format");
+ }
+
+ mc::MCInst Result(mc::MCOpcode::fromValue(Instruction.opcode().value()));
+ Result.setFlags(Instruction.flags().rawValue());
+ Result.setSourceIdentity(Instruction.stableID().value());
+ Result.reserveOperands(*RequiredOperands);
+ for (const MachineOperand &Operand : Instruction.operands()) {
+ if (Result.operands().size() == *RequiredOperands)
+ break;
+ if (Operand.isRegister() && Operand.isImplicit())
+ continue;
+ if (Operand.kind() == MachineOperand::Kind::RegisterMask ||
+ Operand.kind() == MachineOperand::Kind::LiveOutRegisterMask) {
+ continue;
+ }
+ auto Lowered = lowerOperand(Operand, Instruction.function(), Context);
+ if (!Lowered)
+ return std::unexpected(Lowered.error());
+ Result.addOperand(std::move(*Lowered));
+ }
+ if (Result.operands().size() != *RequiredOperands) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "RISC-V instruction has too few encoding operands");
+ }
+ return Result;
+}
+
+Expected<void> RISCVMCTargetLowering::emitTerminalInstruction(const MachineInstr &Instruction,
+ MachineMCContext &Context,
+ mc::MCStreamer &Streamer) const {
+ if (Instruction.opcode() != opcode(GenericOpcode::INLINEASM) &&
+ Instruction.opcode() != opcode(GenericOpcode::INLINEASM_BR)) {
+ return TargetMCInstLowering::emitTerminalInstruction(Instruction, Context, Streamer);
+ }
+ for (const MachineOperand &Operand : Instruction.operands()) {
+ if (Operand.kind() == MachineOperand::Kind::RegisterMask ||
+ Operand.kind() == MachineOperand::Kind::LiveOutRegisterMask ||
+ (Operand.isRegister() && Operand.isImplicit())) {
+ continue;
+ }
+ const auto *Text = Operand.getIf<MCSymbolOperand>();
+ if (Text == nullptr || Text->Offset != 0 || Operand.targetFlags() != 0) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RISC-V inline assembly requires a literal MC-symbol text operand");
+ }
+ return Streamer.emitInlineAssembly(Text->Name);
+ }
+ return {};
+}
+
+Expected<mc::MCSectionDesc> RISCVMCTargetLowering::textSection(const MachineFunction &) const {
+ return textSectionDescription();
+}
+
+Expected<mc::MCSectionDesc> RISCVMCTargetLowering::globalSection(const GlobalObjectDesc &Object,
+ MachineMCContext &Context) const {
+ if (Object.AddressSpace != 0) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC assembly does not support nonzero global address spaces");
+ }
+ if (Object.TargetFlags != 0 || !Object.TargetAttributes.empty()) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC assembly does not recognize this global target state");
+ }
+ const bool IsTLS = Object.TLSModel != GlobalTLSModel::None;
+ const bool IsZero = Object.Initializer && initializerIsZero(*Object.Initializer);
+ const bool HasRelocation = Object.Initializer && initializerHasRelocation(*Object.Initializer);
+
+ mc::MCSectionDesc Result;
+ Result.Alignment = Object.Align.bytes();
+ if (IsTLS) {
+ Result.Name = IsZero ? ".tbss" : ".tdata";
+ Result.Kind = IsZero ? mc::MCSectionKind::TLSBSS : mc::MCSectionKind::TLSData;
+ Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable, mc::MCSectionFlag::TLS};
+ } else if (Object.IsConstant && HasRelocation) {
+ Result.Name = ".data.rel.ro";
+ Result.Kind = mc::MCSectionKind::Data;
+ Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable};
+ } else if (Object.IsConstant) {
+ Result = readOnlySectionDescription();
+ Result.Alignment = Object.Align.bytes();
+ } else if (IsZero) {
+ Result.Name = ".bss";
+ Result.Kind = mc::MCSectionKind::BSS;
+ Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable};
+ } else {
+ Result.Name = ".data";
+ Result.Kind = mc::MCSectionKind::Data;
+ Result.Flags = {mc::MCSectionFlag::Alloc, mc::MCSectionFlag::Writable};
+ }
+
+ if (Object.Section)
+ Result.Name = *Object.Section;
+ if (Object.COMDAT) {
+ auto Group = Context.symbol(*Object.COMDAT);
+ if (!Group)
+ return std::unexpected(Group.error());
+ Result.Flags.set(mc::MCSectionFlag::Group);
+ Result.Group = std::move(*Group);
+ if (!Object.Section)
+ Result.Name += "." + Object.Symbol;
+ }
+ return Result;
+}
+
+Expected<mc::MCSectionDesc>
+RISCVMCTargetLowering::constantPoolSection(const MachineFunction &Function,
+ const MachineConstantPoolEntry &Entry) const {
+ const MachineConstantPoolEntryDesc *Description = Entry.description();
+ if (Description == nullptr)
+ return makeUnexpected(ErrorCode::InvalidArgument, "constant-pool entry is not live");
+ if (Description->Placement.Section == ConstantPoolSectionKind::TextAdjacent)
+ return textSectionDescription();
+
+ mc::MCSectionDesc Result = readOnlySectionDescription();
+ if (Description->Placement.Section == ConstantPoolSectionKind::Data) {
+ Result.Name = ".data";
+ Result.Kind = mc::MCSectionKind::Data;
+ Result.Flags.set(mc::MCSectionFlag::Writable);
+ } else if (Description->Placement.Section == ConstantPoolSectionKind::Target) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC constant pool has an unknown target section");
+ } else if (Description->Placement.Section == ConstantPoolSectionKind::MergeableReadOnly) {
+ Result.Name = ".rodata.cst" + std::to_string(Description->Size);
+ Result.Flags.set(mc::MCSectionFlag::Merge);
+ Result.EntrySize = Description->Size;
+ }
+ if (Description->Placement.UniqueSection) {
+ Result.Name += "." + std::to_string(Function.stableID().value()) + "." +
+ std::to_string(Entry.index().value());
+ Result.UniqueID = static_cast<std::uint64_t>(Entry.index().value()) + 1U;
+ }
+ Result.Alignment = Description->Align.bytes();
+ return Result;
+}
+
+Expected<mc::MCSectionDesc>
+RISCVMCTargetLowering::jumpTableSection(const MachineFunction &Function,
+ const MachineJumpTableEntry &Entry) const {
+ const MachineJumpTableDesc *Description = Entry.description();
+ if (Description == nullptr)
+ return makeUnexpected(ErrorCode::InvalidArgument, "jump-table entry is not live");
+ mc::MCSectionDesc Result = Description->Placement.InTextSection ? textSectionDescription()
+ : readOnlySectionDescription();
+ if (Description->Placement.UniqueSection) {
+ Result.Name += "." + std::to_string(Function.stableID().value()) + ".jt." +
+ std::to_string(Entry.index().value());
+ Result.UniqueID = static_cast<std::uint64_t>(Entry.index().value()) + 1U;
+ }
+ Result.Alignment = Description->Align.bytes();
+ return Result;
+}
+
+Expected<void> RISCVMCTargetLowering::emitFileHeader(MachineMCContext &,
+ mc::MCStreamer &Streamer) const {
+ auto Attribute = Streamer.emitDirective("attribute", "arch, \"rv64gc\"");
+ if (!Attribute)
+ return std::unexpected(Attribute.error());
+ return Streamer.emitDirective("attribute", "stack_align, 16");
+}
+
+Expected<void> RISCVMCTargetLowering::emitFileTrailer(MachineMCContext &Context,
+ mc::MCStreamer &Streamer) const {
+ mc::MCSectionDesc Note{
+ .Name = ".note.GNU-stack",
+ .Kind = mc::MCSectionKind::Custom,
+ .Flags = {},
+ .Alignment = 1,
+ .EntrySize = 0,
+ .Group = {},
+ .UniqueID = std::nullopt,
+ };
+ auto Section = Context.context().getOrCreateSection(std::move(Note));
+ if (!Section)
+ return std::unexpected(Section.error());
+ return Streamer.switchSection(std::move(*Section));
+}
+
+Expected<void> RISCVMCTargetLowering::emitGenericConstant(const GenericConstantData &Constant,
+ std::uint64_t Size,
+ MachineMCContext &Context,
+ mc::MCStreamer &Streamer) const {
+ auto Verification = Constant.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ auto Layout = layoutType(Constant.Type);
+ if (!Layout)
+ return std::unexpected(Layout.error());
+ if (Layout->Size != Size) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "generic constant size does not match the RV64GC data layout");
+ }
+ return emitGenericConstantImpl(Constant, *Layout, Context, Streamer);
+}
+
+Expected<void> RISCVMCTargetLowering::emitGlobalInitializer(const GlobalInitializer &Initializer,
+ MachineMCContext &Context,
+ mc::MCStreamer &Streamer) const {
+ auto Verification = Initializer.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ return emitGlobalInitializerImpl(Initializer, Context, Streamer);
+}
+
+Expected<void> RISCVMCTargetLowering::emitTargetConstant(const TargetConstant &Constant,
+ MachineMCContext &,
+ mc::MCStreamer &Streamer) const {
+ auto Emission = Constant.emit();
+ if (!Emission)
+ return std::unexpected(Emission.error());
+ if (!Emission->Fixups.empty()) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC target-constant fixups require the native object stage");
+ }
+ std::vector<std::uint8_t> Bytes;
+ Bytes.reserve(Emission->Bytes.size());
+ for (const std::byte Byte : Emission->Bytes)
+ Bytes.push_back(std::to_integer<std::uint8_t>(Byte));
+ return Streamer.emitBytes(Bytes);
+}
+
+Expected<void> RISCVMCTargetLowering::emitTargetJumpTable(const MachineFunction &Function,
+ const MachineJumpTableEntry &Entry,
+ MachineMCContext &Context,
+ mc::MCStreamer &Streamer) const {
+ const MachineJumpTableDesc *Description = Entry.description();
+ if (Description == nullptr)
+ return makeUnexpected(ErrorCode::InvalidArgument, "jump-table entry is not live");
+ if (Description->Encoding == JumpTableEntryEncoding::Target) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "RV64GC jump table has an unknown target encoding");
+ }
+
+ mc::MCSymbolRef BaseSymbol;
+ if (Description->Encoding == JumpTableEntryEncoding::GPRelative32BlockAddress ||
+ Description->Encoding == JumpTableEntryEncoding::GPRelative64BlockAddress) {
+ auto GlobalPointer = Context.symbol("__global_pointer$");
+ if (!GlobalPointer)
+ return std::unexpected(GlobalPointer.error());
+ BaseSymbol = std::move(*GlobalPointer);
+ } else if (Description->Encoding == JumpTableEntryEncoding::Custom32) {
+ BaseSymbol = Context.jumpTableSymbol(Function, Entry.index());
+ }
+
+ for (const MachineBasicBlock *TargetBlock : Description->Targets) {
+ if (TargetBlock == nullptr)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "jump table has a null target");
+ const mc::MCSymbolRef TargetSymbol = Context.blockSymbol(*TargetBlock);
+ if (!TargetSymbol)
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "jump-table target has no MC symbol");
+ auto Expression = mc::MCExpr::symbol(TargetSymbol);
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ if (BaseSymbol) {
+ auto Base = mc::MCExpr::symbol(BaseSymbol);
+ if (!Base)
+ return std::unexpected(Base.error());
+ Expression = mc::MCExpr::binary(mc::MCBinaryOperator::Subtract, std::move(*Expression),
+ std::move(*Base));
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ }
+ auto Value = Streamer.emitValue(std::move(*Expression), Description->EntrySize);
+ if (!Value)
+ return std::unexpected(Value.error());
+ }
+ return {};
+}
+
+} // namespace rcc::riscv