aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/MC
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-09 18:31:49 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-09 18:31:49 +0800
commit379ff50dcc7ccf977379aee68fa9d10f69065d23 (patch)
treee0fac3952d46d0bbcaea32bcfc6087c95fff2992 /backend/lib/MC
parent495c675771f43cfa12ab7a426a538be72fec1805 (diff)
downloadrcc-project-379ff50dcc7ccf977379aee68fa9d10f69065d23.tar.gz
rcc-project-379ff50dcc7ccf977379aee68fa9d10f69065d23.tar.xz
[MC] Implement RV64GC assembly emission
Diffstat (limited to 'backend/lib/MC')
-rw-r--r--backend/lib/MC/CMakeLists.txt58
-rw-r--r--backend/lib/MC/MCCFI.cpp70
-rw-r--r--backend/lib/MC/MCContext.cpp95
-rw-r--r--backend/lib/MC/MCExpr.cpp205
-rw-r--r--backend/lib/MC/MCInstPrinter.cpp170
-rw-r--r--backend/lib/MC/MCSection.cpp60
-rw-r--r--backend/lib/MC/MCStreamer.cpp488
7 files changed, 1146 insertions, 0 deletions
diff --git a/backend/lib/MC/CMakeLists.txt b/backend/lib/MC/CMakeLists.txt
new file mode 100644
index 0000000..a55b468
--- /dev/null
+++ b/backend/lib/MC/CMakeLists.txt
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+set(
+ RCCBackendMCSources
+ MCCFI.cpp
+ MCContext.cpp
+ MCExpr.cpp
+ MCInstPrinter.cpp
+ MCSection.cpp
+ MCStreamer.cpp
+)
+set(
+ RCCBackendMCHeaders
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCCFI.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/MCInst.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCInstPrinter.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"
+)
+
+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)
+
+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})
+rcc_backend_register_tidy_files(${RCCBackendMCSourcePaths})
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/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/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/MCSection.cpp b/backend/lib/MC/MCSection.cpp
new file mode 100644
index 0000000..21ba410
--- /dev/null
+++ b/backend/lib/MC/MCSection.cpp
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCSection.h"
+
+#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;
+}
+
+[[nodiscard]] bool isPowerOfTwo(std::uint64_t Value) noexcept {
+ return Value != 0 && (Value & (Value - 1U)) == 0;
+}
+
+} // namespace
+
+Expected<void> MCSectionDesc::verify() const {
+ if (!isValidAssemblyName(Name))
+ return makeUnexpected(ErrorCode::InvalidArgument, "MC section has an invalid name");
+ if (!isPowerOfTwo(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..001fa28
--- /dev/null
+++ b/backend/lib/MC/MCStreamer.cpp
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MC/MCStreamer.h"
+
+#include <algorithm>
+#include <array>
+#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]] bool isPowerOfTwo(std::uint64_t Value) noexcept {
+ return Value != 0 && (Value & (Value - 1U)) == 0;
+}
+
+[[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 (!isPowerOfTwo(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::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