aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/AsmPrinter
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/AsmPrinter
parent495c675771f43cfa12ab7a426a538be72fec1805 (diff)
downloadrcc-project-379ff50dcc7ccf977379aee68fa9d10f69065d23.tar.gz
rcc-project-379ff50dcc7ccf977379aee68fa9d10f69065d23.tar.xz
[MC] Implement RV64GC assembly emission
Diffstat (limited to 'backend/lib/AsmPrinter')
-rw-r--r--backend/lib/AsmPrinter/AsmPrinter.cpp679
-rw-r--r--backend/lib/AsmPrinter/CMakeLists.txt58
-rw-r--r--backend/lib/AsmPrinter/MachineMCContext.cpp153
-rw-r--r--backend/lib/AsmPrinter/TargetMCInstLowering.cpp49
4 files changed, 939 insertions, 0 deletions
diff --git a/backend/lib/AsmPrinter/AsmPrinter.cpp b/backend/lib/AsmPrinter/AsmPrinter.cpp
new file mode 100644
index 0000000..cebd6b8
--- /dev/null
+++ b/backend/lib/AsmPrinter/AsmPrinter.cpp
@@ -0,0 +1,679 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/AsmPrinter.h"
+
+#include "rcc/CodeGen/CompilationUnit.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/MachineCFIInfo.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/MachineVerifier.h"
+#include "rcc/CodeGen/TargetConstant.h"
+#include "rcc/CodeGen/TargetDescription.h"
+#include "rcc/CodeGen/TargetMCInstLowering.h"
+#include "rcc/MC/MCCFI.h"
+#include "rcc/MC/MCContext.h"
+#include "rcc/MC/MCExpr.h"
+#include "rcc/MC/MCSection.h"
+#include "rcc/MC/MCStreamer.h"
+#include "rcc/MC/MCSymbol.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <variant>
+
+namespace rcc {
+namespace {
+
+[[nodiscard]] Expected<mc::MCExprRef> symbolExpression(mc::MCSymbolRef Symbol) {
+ auto Expression = mc::MCExpr::symbol(std::move(Symbol));
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ return *Expression;
+}
+
+[[nodiscard]] Expected<mc::MCExprRef> addConstant(mc::MCExprRef Expression, std::int64_t Addend) {
+ if (Addend == 0)
+ return Expression;
+ auto Result = mc::MCExpr::binary(mc::MCBinaryOperator::Add, std::move(Expression),
+ mc::MCExpr::constant(Addend));
+ if (!Result)
+ return std::unexpected(Result.error());
+ return *Result;
+}
+
+[[nodiscard]] Expected<mc::MCExprRef>
+symbolDifference(mc::MCSymbolRef Minuend, mc::MCSymbolRef Subtrahend, std::int64_t Addend) {
+ auto Left = symbolExpression(std::move(Minuend));
+ if (!Left)
+ return std::unexpected(Left.error());
+ auto Right = symbolExpression(std::move(Subtrahend));
+ if (!Right)
+ return std::unexpected(Right.error());
+ auto Difference =
+ mc::MCExpr::binary(mc::MCBinaryOperator::Subtract, std::move(*Left), std::move(*Right));
+ if (!Difference)
+ return std::unexpected(Difference.error());
+ return addConstant(std::move(*Difference), Addend);
+}
+
+[[nodiscard]] Expected<mc::MCCFIInstruction>
+lowerCFIInstruction(const MachineCFIInstruction &Instruction, MachineMCContext &Context) {
+ mc::MCCFIInstruction Result;
+ const auto Register = [&Result](const MachineCFIRegisterRule &Rule) {
+ Result.Operands.push_back(static_cast<std::int64_t>(Rule.Register.value()));
+ };
+ const auto Offset = [&Result](const MachineCFIOffsetRule &Rule) {
+ Result.Operands.push_back(Rule.Offset);
+ };
+ const auto RegisterOffset = [&Result](const MachineCFIRegisterOffsetRule &Rule) {
+ Result.Operands.push_back(static_cast<std::int64_t>(Rule.Register.value()));
+ Result.Operands.push_back(Rule.Offset);
+ };
+
+ switch (Instruction.operation()) {
+ case MachineCFIOperation::SameValue:
+ Result.Operation = mc::MCCFIOperation::SameValue;
+ Register(*Instruction.getIf<MachineCFIRegisterRule>());
+ break;
+ case MachineCFIOperation::RememberState:
+ Result.Operation = mc::MCCFIOperation::RememberState;
+ break;
+ case MachineCFIOperation::RestoreState:
+ Result.Operation = mc::MCCFIOperation::RestoreState;
+ break;
+ case MachineCFIOperation::Offset:
+ Result.Operation = mc::MCCFIOperation::Offset;
+ RegisterOffset(*Instruction.getIf<MachineCFIRegisterOffsetRule>());
+ break;
+ case MachineCFIOperation::DefCFARegister:
+ Result.Operation = mc::MCCFIOperation::DefCFARegister;
+ Register(*Instruction.getIf<MachineCFIRegisterRule>());
+ break;
+ case MachineCFIOperation::DefCFAOffset:
+ Result.Operation = mc::MCCFIOperation::DefCFAOffset;
+ Offset(*Instruction.getIf<MachineCFIOffsetRule>());
+ break;
+ case MachineCFIOperation::DefCFA:
+ Result.Operation = mc::MCCFIOperation::DefCFA;
+ RegisterOffset(*Instruction.getIf<MachineCFIRegisterOffsetRule>());
+ break;
+ case MachineCFIOperation::RelativeOffset:
+ Result.Operation = mc::MCCFIOperation::RelativeOffset;
+ RegisterOffset(*Instruction.getIf<MachineCFIRegisterOffsetRule>());
+ break;
+ case MachineCFIOperation::AdjustCFAOffset:
+ Result.Operation = mc::MCCFIOperation::AdjustCFAOffset;
+ Offset(*Instruction.getIf<MachineCFIOffsetRule>());
+ break;
+ case MachineCFIOperation::Escape:
+ Result.Operation = mc::MCCFIOperation::Escape;
+ Result.Bytes = Instruction.getIf<MachineCFIEscapeRule>()->Bytes;
+ break;
+ case MachineCFIOperation::Restore:
+ Result.Operation = mc::MCCFIOperation::Restore;
+ Register(*Instruction.getIf<MachineCFIRegisterRule>());
+ break;
+ case MachineCFIOperation::Undefined:
+ Result.Operation = mc::MCCFIOperation::Undefined;
+ Register(*Instruction.getIf<MachineCFIRegisterRule>());
+ break;
+ case MachineCFIOperation::Register: {
+ Result.Operation = mc::MCCFIOperation::Register;
+ const auto &Rule = *Instruction.getIf<MachineCFIRegisterRegisterRule>();
+ Result.Operands.push_back(static_cast<std::int64_t>(Rule.Register.value()));
+ Result.Operands.push_back(static_cast<std::int64_t>(Rule.SourceRegister.value()));
+ break;
+ }
+ case MachineCFIOperation::WindowSave:
+ Result.Operation = mc::MCCFIOperation::WindowSave;
+ break;
+ case MachineCFIOperation::NegateReturnAddressState:
+ Result.Operation = mc::MCCFIOperation::NegateReturnAddressState;
+ break;
+ case MachineCFIOperation::NegateReturnAddressStateWithPC:
+ Result.Operation = mc::MCCFIOperation::NegateReturnAddressStateWithPC;
+ break;
+ case MachineCFIOperation::GNUArgsSize:
+ Result.Operation = mc::MCCFIOperation::GNUArgsSize;
+ Offset(*Instruction.getIf<MachineCFIOffsetRule>());
+ break;
+ case MachineCFIOperation::Label: {
+ Result.Operation = mc::MCCFIOperation::Label;
+ auto Symbol = Context.symbol(Instruction.getIf<MachineCFILabelRule>()->Symbol,
+ mc::MCSymbolKind::Temporary);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ Result.Symbol = std::move(*Symbol);
+ break;
+ }
+ case MachineCFIOperation::ValueOffset:
+ Result.Operation = mc::MCCFIOperation::ValueOffset;
+ RegisterOffset(*Instruction.getIf<MachineCFIRegisterOffsetRule>());
+ break;
+ case MachineCFIOperation::LLVMDefAddressSpaceCFA:
+ case MachineCFIOperation::LLVMRegisterPair:
+ case MachineCFIOperation::LLVMVectorRegisters:
+ case MachineCFIOperation::LLVMVectorOffset:
+ case MachineCFIOperation::LLVMVectorRegisterMask:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "assembly emission does not support this LLVM-specific CFI operation");
+ case MachineCFIOperation::Count:
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "machine CFI instruction has an invalid operation");
+ }
+
+ auto Verification = Result.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ return Result;
+}
+
+[[nodiscard]] Expected<void> emitNamedLabel(const MachineOperand &Operand,
+ MachineMCContext &Context, mc::MCStreamer &Streamer) {
+ const auto *SymbolOperand = Operand.getIf<MCSymbolOperand>();
+ if (SymbolOperand == nullptr || SymbolOperand->Offset != 0 || Operand.targetFlags() != 0) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "label pseudo requires an unmodified MC symbol operand");
+ }
+ auto Symbol = Context.symbol(SymbolOperand->Name, mc::MCSymbolKind::Temporary);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ return Streamer.emitLabel(std::move(*Symbol));
+}
+
+[[nodiscard]] bool validValueSize(std::uint64_t Size) noexcept {
+ return Size == 1 || Size == 2 || Size == 4 || Size == 8;
+}
+
+[[nodiscard]] Expected<void> emitGlobalAttributes(const GlobalObjectDesc &Object,
+ const mc::MCSymbolRef &Symbol,
+ mc::MCStreamer &Streamer) {
+ switch (Object.Linkage) {
+ case GlobalLinkage::External:
+ case GlobalLinkage::Appending:
+ case GlobalLinkage::Common: {
+ auto Emitted = Streamer.emitSymbolAttribute(Symbol, mc::MCSymbolAttribute::Global);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ break;
+ }
+ case GlobalLinkage::LinkOnceAny:
+ case GlobalLinkage::LinkOnceODR:
+ case GlobalLinkage::WeakAny:
+ case GlobalLinkage::WeakODR:
+ case GlobalLinkage::ExternalWeak: {
+ auto Emitted = Streamer.emitSymbolAttribute(Symbol, mc::MCSymbolAttribute::Weak);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ break;
+ }
+ case GlobalLinkage::Internal:
+ case GlobalLinkage::Private: {
+ auto Emitted = Streamer.emitSymbolAttribute(Symbol, mc::MCSymbolAttribute::Local);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ break;
+ }
+ case GlobalLinkage::AvailableExternally:
+ break;
+ case GlobalLinkage::Count:
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "global object has invalid linkage during assembly emission");
+ }
+
+ if (Object.Visibility == GlobalVisibility::Hidden) {
+ auto Emitted = Streamer.emitSymbolAttribute(Symbol, mc::MCSymbolAttribute::Hidden);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ } else if (Object.Visibility == GlobalVisibility::Protected) {
+ auto Emitted = Streamer.emitSymbolAttribute(Symbol, mc::MCSymbolAttribute::Protected);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ }
+ return Streamer.emitSymbolAttribute(Symbol, Object.TLSModel == GlobalTLSModel::None
+ ? mc::MCSymbolAttribute::Object
+ : mc::MCSymbolAttribute::TLSObject);
+}
+
+} // namespace
+
+Expected<void> AsmPrinter::verifyFunction(const MachineFunction &Function) const {
+ const MachineFunctionProperties Required = {
+ MachineFunctionProperty::NoPHIs,
+ MachineFunctionProperty::Legalized,
+ MachineFunctionProperty::RegBankSelected,
+ MachineFunctionProperty::Selected,
+ MachineFunctionProperty::TiedOpsRewritten,
+ MachineFunctionProperty::NoVRegs,
+ MachineFunctionProperty::FrameFinalized,
+ MachineFunctionProperty::NoFrameIndices,
+ MachineFunctionProperty::NoExecutablePseudos,
+ };
+ if (!Function.properties().containsAll(Required)) {
+ const MachineFunctionProperties Missing = Required.missingFrom(Function.properties());
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "assembly emission requires finalized MIR properties: " +
+ Missing.toString());
+ }
+ if (!Options.VerifyInput)
+ return {};
+ return MachineVerifier::verify(Function, *Target,
+ MachineVerifierOptions{.Mode = MachineVerifierMode::Generic,
+ .Stage = PipelineStage::Emission,
+ .VerifyProperties = true});
+}
+
+Expected<void> AsmPrinter::emitCompilationUnit(const CompilationUnit &Unit) {
+ if (UnitEmissionStarted) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "a compilation unit can only be emitted once by an AsmPrinter");
+ }
+ UnitEmissionStarted = true;
+
+ auto Initialized = Context->initialize(Unit);
+ if (!Initialized)
+ return std::unexpected(Initialized.error());
+ auto Header = Lowering->emitFileHeader(*Context, *Streamer);
+ if (!Header)
+ return std::unexpected(Header.error());
+
+ auto Globals = emitGlobalObjects(Unit);
+ if (!Globals)
+ return std::unexpected(Globals.error());
+
+ for (std::size_t Index = 0; Index < Unit.functionCount(); ++Index) {
+ const MachineFunction *Function = Unit.functionAt(Index);
+ if (Function == nullptr) {
+ return makeUnexpected(ErrorCode::InternalError,
+ "compilation unit contains a null function during assembly emission");
+ }
+ auto Emitted = emitFunction(*Function);
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ }
+
+ auto Trailer = Lowering->emitFileTrailer(*Context, *Streamer);
+ if (!Trailer)
+ return std::unexpected(Trailer.error());
+ auto Finished = Streamer->finish();
+ if (!Finished)
+ return std::unexpected(Finished.error());
+ UnitEmissionFinished = true;
+ return {};
+}
+
+Expected<void> AsmPrinter::emitGlobalObjects(const CompilationUnit &Unit) {
+ for (std::size_t Index = 0; Index < Unit.globalObjectCount(); ++Index) {
+ const GlobalObjectDesc *Object = Unit.globalObjectAt(Index);
+ if (Object == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "compilation unit contains a null global");
+ if (Object->isDeclaration() || Object->Linkage == GlobalLinkage::AvailableExternally)
+ continue;
+
+ const mc::MCSymbolRef Symbol = Context->globalSymbol(*Object);
+ if (!Symbol)
+ return makeUnexpected(ErrorCode::InternalError, "global object has no MC symbol");
+ auto Attributes = emitGlobalAttributes(*Object, Symbol, *Streamer);
+ if (!Attributes)
+ return std::unexpected(Attributes.error());
+
+ if (Object->Linkage == GlobalLinkage::Common) {
+ auto Name = mc::printMCSymbolName(Symbol->name());
+ if (!Name)
+ return std::unexpected(Name.error());
+ auto Common =
+ Streamer->emitDirective("comm", *Name + "," + std::to_string(Object->Size) + "," +
+ std::to_string(Object->Align.bytes()));
+ if (!Common)
+ return std::unexpected(Common.error());
+ continue;
+ }
+
+ auto Description = Lowering->globalSection(*Object, *Context);
+ if (!Description)
+ return std::unexpected(Description.error());
+ auto Section = Context->context().getOrCreateSection(std::move(*Description));
+ if (!Section)
+ return std::unexpected(Section.error());
+ auto Switched = Streamer->switchSection(std::move(*Section));
+ if (!Switched)
+ return std::unexpected(Switched.error());
+ auto Aligned = Streamer->emitAlignment(Object->Align.bytes());
+ if (!Aligned)
+ return std::unexpected(Aligned.error());
+ auto Label = Streamer->emitLabel(Symbol);
+ if (!Label)
+ return std::unexpected(Label.error());
+ if (!Object->Initializer) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "defined global object has no initializer");
+ }
+ auto Initializer = Lowering->emitGlobalInitializer(*Object->Initializer, *Context, *Streamer);
+ if (!Initializer)
+ return std::unexpected(Initializer.error());
+ auto Size = Streamer->emitSymbolSize(
+ Symbol, mc::MCExpr::constant(static_cast<std::int64_t>(Object->Size)));
+ if (!Size)
+ return std::unexpected(Size.error());
+ }
+ return {};
+}
+
+Expected<void> AsmPrinter::emitFunction(const MachineFunction &Function) {
+ if (UnitEmissionFinished) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "cannot emit a function after the assembly stream is finalized");
+ }
+ auto Verification = verifyFunction(Function);
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ auto Initialized = Context->initializeFunction(Function);
+ if (!Initialized)
+ return std::unexpected(Initialized.error());
+
+ auto SectionDescription = Lowering->textSection(Function);
+ if (!SectionDescription)
+ return std::unexpected(SectionDescription.error());
+ auto Section = Context->context().getOrCreateSection(std::move(*SectionDescription));
+ if (!Section)
+ return std::unexpected(Section.error());
+ auto Switched = Streamer->switchSection(std::move(*Section));
+ if (!Switched)
+ return std::unexpected(Switched.error());
+
+ const mc::MCSymbolRef FunctionSymbol = Context->functionSymbol(Function);
+ if (!FunctionSymbol) {
+ return makeUnexpected(ErrorCode::InternalError,
+ "function has no MC symbol after context initialization");
+ }
+ if (Options.EmitFunctionDirectives) {
+ auto Global = Streamer->emitSymbolAttribute(FunctionSymbol, mc::MCSymbolAttribute::Global);
+ if (!Global)
+ return std::unexpected(Global.error());
+ auto Type = Streamer->emitSymbolAttribute(FunctionSymbol, mc::MCSymbolAttribute::Function);
+ if (!Type)
+ return std::unexpected(Type.error());
+ }
+ if (const auto Alignment = Function.attributes().alignment()) {
+ auto Aligned = Streamer->emitAlignment(Alignment->bytes());
+ if (!Aligned)
+ return std::unexpected(Aligned.error());
+ }
+ auto FunctionLabel = Streamer->emitLabel(FunctionSymbol);
+ if (!FunctionLabel)
+ return std::unexpected(FunctionLabel.error());
+ if (Options.EmitCFI) {
+ mc::MCCFIInstruction StartProc;
+ StartProc.Operation = mc::MCCFIOperation::StartProc;
+ auto CFIStart = Streamer->emitCFI(StartProc);
+ if (!CFIStart)
+ return std::unexpected(CFIStart.error());
+ }
+
+ auto Body = emitFunctionBody(Function);
+ if (!Body)
+ return std::unexpected(Body.error());
+ if (Options.EmitCFI) {
+ mc::MCCFIInstruction EndProc;
+ EndProc.Operation = mc::MCCFIOperation::EndProc;
+ auto CFIEnd = Streamer->emitCFI(EndProc);
+ if (!CFIEnd)
+ return std::unexpected(CFIEnd.error());
+ }
+
+ auto EndSymbol = Context->symbol(".Lfunc_end" + std::to_string(Function.stableID().value()),
+ mc::MCSymbolKind::Temporary);
+ if (!EndSymbol)
+ return std::unexpected(EndSymbol.error());
+ auto EndLabel = Streamer->emitLabel(*EndSymbol);
+ if (!EndLabel)
+ return std::unexpected(EndLabel.error());
+ if (Options.EmitFunctionDirectives) {
+ auto Size = symbolDifference(*EndSymbol, FunctionSymbol, 0);
+ if (!Size)
+ return std::unexpected(Size.error());
+ auto EmittedSize = Streamer->emitSymbolSize(FunctionSymbol, std::move(*Size));
+ if (!EmittedSize)
+ return std::unexpected(EmittedSize.error());
+ }
+
+ auto Constants = emitConstantPool(Function);
+ if (!Constants)
+ return std::unexpected(Constants.error());
+ return emitJumpTables(Function);
+}
+
+Expected<void> AsmPrinter::emitFunctionBody(const MachineFunction &Function) {
+ for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
+ Block = Block->next()) {
+ if (Block->attributes().alignment().bytes() > 1) {
+ auto Aligned = Streamer->emitAlignment(Block->attributes().alignment().bytes());
+ if (!Aligned)
+ return std::unexpected(Aligned.error());
+ }
+ if (Options.EmitBlockLabels) {
+ const mc::MCSymbolRef Symbol = Context->blockSymbol(*Block);
+ if (!Symbol)
+ return makeUnexpected(ErrorCode::InternalError, "machine block has no MC symbol");
+ auto Label = Streamer->emitLabel(Symbol);
+ if (!Label)
+ return std::unexpected(Label.error());
+ }
+
+ for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
+ Instruction = Instruction->next()) {
+ const InstructionDescriptor *Descriptor =
+ Target->instructionDescriptor(Instruction->opcode());
+ if (Descriptor == nullptr) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "assembly emission encountered an unknown opcode");
+ }
+ if (Options.EmitInstructionSymbols) {
+ if (const auto SymbolName = Instruction->preInstrSymbol()) {
+ auto Symbol = Context->symbol(std::string(*SymbolName), mc::MCSymbolKind::Temporary);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ auto Label = Streamer->emitLabel(std::move(*Symbol));
+ if (!Label)
+ return std::unexpected(Label.error());
+ }
+ }
+
+ Expected<void> Emitted;
+ if (Instruction->opcode() == opcode(GenericOpcode::CFI_INSTRUCTION)) {
+ const auto *Index = Instruction->operandAt(0) == nullptr
+ ? nullptr
+ : Instruction->operandAt(0)->getIf<CFIIndexOperand>();
+ const MachineCFIInstruction *CFI =
+ Index == nullptr ? nullptr : Function.cfiInfo().instruction(Index->Index);
+ if (CFI == nullptr) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "CFI pseudo references an invalid CFI instruction");
+ }
+ if (Options.EmitCFI) {
+ auto Lowered = lowerCFIInstruction(*CFI, *Context);
+ if (!Lowered)
+ return std::unexpected(Lowered.error());
+ Emitted = Streamer->emitCFI(*Lowered);
+ }
+ } else if (Instruction->opcode() == opcode(GenericOpcode::EH_LABEL) ||
+ Instruction->opcode() == opcode(GenericOpcode::GC_LABEL) ||
+ Instruction->opcode() == opcode(GenericOpcode::ANNOTATION_LABEL)) {
+ if (Instruction->operandAt(0) == nullptr) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "label pseudo has no symbol operand");
+ }
+ Emitted = emitNamedLabel(*Instruction->operandAt(0), *Context, *Streamer);
+ } else if (Descriptor->Flags.has(InstructionFlag::TerminalEmission)) {
+ Emitted = Lowering->emitTerminalInstruction(*Instruction, *Context, *Streamer);
+ } else if (Descriptor->Flags.has(InstructionFlag::Meta)) {
+ Emitted = {};
+ } else if (Descriptor->Flags.has(InstructionFlag::ProducesCode)) {
+ auto Lowered = Lowering->lowerInstruction(*Instruction, *Context);
+ if (!Lowered)
+ return std::unexpected(Lowered.error());
+ Emitted = Streamer->emitInstruction(*Lowered);
+ } else {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "non-meta instruction has no assembly emission behavior");
+ }
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+
+ if (Options.EmitInstructionSymbols) {
+ if (const auto SymbolName = Instruction->postInstrSymbol()) {
+ auto Symbol = Context->symbol(std::string(*SymbolName), mc::MCSymbolKind::Temporary);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ auto Label = Streamer->emitLabel(std::move(*Symbol));
+ if (!Label)
+ return std::unexpected(Label.error());
+ }
+ }
+ }
+ }
+ return {};
+}
+
+Expected<void> AsmPrinter::emitConstantPool(const MachineFunction &Function) {
+ for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
+ const MachineConstantPoolEntryDesc *Description = Entry.description();
+ if (Description == nullptr)
+ continue;
+ auto SectionDescription = Lowering->constantPoolSection(Function, Entry);
+ if (!SectionDescription)
+ return std::unexpected(SectionDescription.error());
+ auto Section = Context->context().getOrCreateSection(std::move(*SectionDescription));
+ if (!Section)
+ return std::unexpected(Section.error());
+ auto Switched = Streamer->switchSection(std::move(*Section));
+ if (!Switched)
+ return std::unexpected(Switched.error());
+ auto Aligned = Streamer->emitAlignment(Description->Align.bytes());
+ if (!Aligned)
+ return std::unexpected(Aligned.error());
+
+ const mc::MCSymbolRef Symbol = Context->constantPoolSymbol(Function, Entry.index());
+ if (!Symbol)
+ return makeUnexpected(ErrorCode::InternalError, "constant-pool entry has no MC symbol");
+ auto Label = Streamer->emitLabel(Symbol);
+ if (!Label)
+ return std::unexpected(Label.error());
+
+ Expected<void> Emitted;
+ if (const auto *Generic = std::get_if<GenericConstantData>(&Description->Value)) {
+ Emitted = Lowering->emitGenericConstant(*Generic, Description->Size, *Context, *Streamer);
+ } else if (const auto *TargetValue = std::get_if<TargetConstant>(&Description->Value)) {
+ Emitted = Lowering->emitTargetConstant(*TargetValue, *Context, *Streamer);
+ } else if (const auto *Difference =
+ std::get_if<SymbolDifferenceConstant>(&Description->Value)) {
+ if (!validValueSize(Description->Size)) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "symbol-difference constant has an unsupported storage size");
+ }
+ auto Minuend = Context->symbol(Difference->Minuend);
+ if (!Minuend)
+ return std::unexpected(Minuend.error());
+ auto Subtrahend = Context->symbol(Difference->Subtrahend);
+ if (!Subtrahend)
+ return std::unexpected(Subtrahend.error());
+ auto Expression = symbolDifference(*Minuend, *Subtrahend, Difference->Addend);
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ Emitted =
+ Streamer->emitValue(std::move(*Expression), static_cast<std::uint8_t>(Description->Size));
+ } else if (const auto *Address = std::get_if<BlockAddressConstant>(&Description->Value)) {
+ if (!validValueSize(Description->Size)) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "block-address constant has an unsupported storage size");
+ }
+ auto AddressSymbol = Context->blockAddressSymbol(Address->FunctionName, Address->BlockName);
+ if (!AddressSymbol)
+ return std::unexpected(AddressSymbol.error());
+ auto Expression = symbolExpression(std::move(*AddressSymbol));
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ Expression = addConstant(std::move(*Expression), Address->Addend);
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ Emitted =
+ Streamer->emitValue(std::move(*Expression), static_cast<std::uint8_t>(Description->Size));
+ } else if (const auto *TargetExpression =
+ std::get_if<TargetExpressionConstant>(&Description->Value)) {
+ Emitted = Lowering->emitTargetConstant(TargetExpression->Expression, *Context, *Streamer);
+ } else {
+ return makeUnexpected(ErrorCode::InternalError, "constant-pool value has an invalid kind");
+ }
+ if (!Emitted)
+ return std::unexpected(Emitted.error());
+ }
+ return {};
+}
+
+Expected<void> AsmPrinter::emitJumpTables(const MachineFunction &Function) {
+ for (const MachineJumpTableEntry &Entry : Function.jumpTableInfo().tables()) {
+ const MachineJumpTableDesc *Description = Entry.description();
+ if (Description == nullptr)
+ continue;
+ auto SectionDescription = Lowering->jumpTableSection(Function, Entry);
+ if (!SectionDescription)
+ return std::unexpected(SectionDescription.error());
+ auto Section = Context->context().getOrCreateSection(std::move(*SectionDescription));
+ if (!Section)
+ return std::unexpected(Section.error());
+ auto Switched = Streamer->switchSection(std::move(*Section));
+ if (!Switched)
+ return std::unexpected(Switched.error());
+ auto Aligned = Streamer->emitAlignment(Description->Align.bytes());
+ if (!Aligned)
+ return std::unexpected(Aligned.error());
+
+ const mc::MCSymbolRef TableSymbol = Context->jumpTableSymbol(Function, Entry.index());
+ if (!TableSymbol)
+ return makeUnexpected(ErrorCode::InternalError, "jump-table entry has no MC symbol");
+ auto Label = Streamer->emitLabel(TableSymbol);
+ if (!Label)
+ return std::unexpected(Label.error());
+
+ if (Description->Encoding != JumpTableEntryEncoding::BlockAddress &&
+ Description->Encoding != JumpTableEntryEncoding::LabelDifference32 &&
+ Description->Encoding != JumpTableEntryEncoding::LabelDifference64) {
+ auto TargetTable = Lowering->emitTargetJumpTable(Function, Entry, *Context, *Streamer);
+ if (!TargetTable)
+ return std::unexpected(TargetTable.error());
+ continue;
+ }
+
+ 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::InternalError, "jump-table target has no MC symbol");
+
+ Expected<mc::MCExprRef> Expression = symbolExpression(TargetSymbol);
+ if (Description->Encoding == JumpTableEntryEncoding::LabelDifference32 ||
+ Description->Encoding == JumpTableEntryEncoding::LabelDifference64)
+ Expression = symbolDifference(TargetSymbol, TableSymbol, 0);
+ 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
diff --git a/backend/lib/AsmPrinter/CMakeLists.txt b/backend/lib/AsmPrinter/CMakeLists.txt
new file mode 100644
index 0000000..6979a71
--- /dev/null
+++ b/backend/lib/AsmPrinter/CMakeLists.txt
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+set(
+ RCCBackendAsmPrinterSources
+ AsmPrinter.cpp
+ MachineMCContext.cpp
+ TargetMCInstLowering.cpp
+)
+set(
+ RCCBackendAsmPrinterHeaders
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/AsmPrinter.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineMCContext.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/TargetMCInstLowering.h"
+)
+
+add_library(RCCBackendAsmPrinter ${RCCBackendAsmPrinterSources})
+add_library(RCCBackend::AsmPrinter ALIAS RCCBackendAsmPrinter)
+
+rcc_backend_configure_target(RCCBackendAsmPrinter)
+
+target_include_directories(
+ RCCBackendAsmPrinter
+ PUBLIC
+ "$<BUILD_INTERFACE:${RCC_BACKEND_SOURCE_DIR}/include>"
+ "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
+)
+target_link_libraries(
+ RCCBackendAsmPrinter
+ PUBLIC
+ RCCBackend::CodeGen
+ RCCBackend::MC
+)
+
+set_target_properties(
+ RCCBackendAsmPrinter
+ PROPERTIES
+ EXPORT_NAME AsmPrinter
+ OUTPUT_NAME RCCBackendAsmPrinter
+)
+
+install(
+ TARGETS RCCBackendAsmPrinter
+ EXPORT RCCBackendTargets
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+)
+
+set(RCCBackendAsmPrinterSourcePaths)
+foreach(Source IN LISTS RCCBackendAsmPrinterSources)
+ list(APPEND RCCBackendAsmPrinterSourcePaths "${CMAKE_CURRENT_SOURCE_DIR}/${Source}")
+endforeach()
+
+rcc_backend_register_format_files(
+ ${RCCBackendAsmPrinterSourcePaths}
+ ${RCCBackendAsmPrinterHeaders}
+)
+rcc_backend_register_tidy_files(${RCCBackendAsmPrinterSourcePaths})
diff --git a/backend/lib/AsmPrinter/MachineMCContext.cpp b/backend/lib/AsmPrinter/MachineMCContext.cpp
new file mode 100644
index 0000000..163e228
--- /dev/null
+++ b/backend/lib/AsmPrinter/MachineMCContext.cpp
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MachineMCContext.h"
+
+#include "rcc/CodeGen/CompilationUnit.h"
+#include "rcc/CodeGen/GlobalObject.h"
+#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineConstantPool.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineJumpTableInfo.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <utility>
+
+namespace rcc {
+
+std::size_t MachineMCContext::FunctionIndexHash::operator()(FunctionIndexKey Key) const noexcept {
+ const std::size_t FunctionHash = std::hash<const MachineFunction *>{}(Key.Function);
+ const std::size_t IndexHash = std::hash<std::uint32_t>{}(Key.Index);
+ return FunctionHash ^ (IndexHash + 0x9e3779b9U + (FunctionHash << 6U) + (FunctionHash >> 2U));
+}
+
+Expected<void> MachineMCContext::initialize(const CompilationUnit &Unit) {
+ auto Verification = Unit.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ for (std::size_t Index = 0; Index < Unit.globalObjectCount(); ++Index) {
+ const GlobalObjectDesc *Object = Unit.globalObjectAt(Index);
+ if (Object == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "compilation unit contains a null global");
+ auto Symbol = Context->getOrCreateSymbol(Object->Symbol, mc::MCSymbolKind::Object);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ GlobalSymbols.emplace(Object, std::move(*Symbol));
+ }
+ for (std::size_t Index = 0; Index < Unit.functionCount(); ++Index) {
+ const MachineFunction *Function = Unit.functionAt(Index);
+ if (Function == nullptr)
+ return makeUnexpected(ErrorCode::InternalError, "compilation unit contains a null function");
+ auto Initialized = initializeFunction(*Function);
+ if (!Initialized)
+ return std::unexpected(Initialized.error());
+ }
+ return {};
+}
+
+Expected<void> MachineMCContext::initializeFunction(const MachineFunction &Function) {
+ if (FunctionSymbols.contains(&Function))
+ return {};
+
+ auto FunctionSymbol =
+ Context->getOrCreateSymbol(std::string(Function.name()), mc::MCSymbolKind::Function);
+ if (!FunctionSymbol)
+ return std::unexpected(FunctionSymbol.error());
+ FunctionSymbols.emplace(&Function, *FunctionSymbol);
+
+ for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
+ Block = Block->next()) {
+ const std::optional<std::size_t> DenseNumber = Block->denseNumber();
+ if (!Block->isLinked() || !DenseNumber) {
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "MC symbol initialization requires linked and numbered blocks");
+ }
+ const std::string Name =
+ ".LBB" + std::to_string(Function.stableID().value()) + "_" + std::to_string(*DenseNumber);
+ auto Symbol = Context->getOrCreateSymbol(Name, mc::MCSymbolKind::BasicBlock);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ BlockSymbols.emplace(Block, *Symbol);
+ }
+
+ for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
+ if (Entry.isTombstone())
+ continue;
+ const std::string Name = ".LCPI" + std::to_string(Function.stableID().value()) + "_" +
+ std::to_string(Entry.index().value());
+ auto Symbol = Context->getOrCreateSymbol(Name, mc::MCSymbolKind::ConstantPool);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ ConstantPoolSymbols.emplace(
+ FunctionIndexKey{.Function = &Function, .Index = Entry.index().value()}, *Symbol);
+ }
+
+ for (const MachineJumpTableEntry &Entry : Function.jumpTableInfo().tables()) {
+ if (Entry.isTombstone() || Entry.isPending())
+ continue;
+ const std::string Name = ".LJTI" + std::to_string(Function.stableID().value()) + "_" +
+ std::to_string(Entry.index().value());
+ auto Symbol = Context->getOrCreateSymbol(Name, mc::MCSymbolKind::JumpTable);
+ if (!Symbol)
+ return std::unexpected(Symbol.error());
+ JumpTableSymbols.emplace(
+ FunctionIndexKey{.Function = &Function, .Index = Entry.index().value()}, *Symbol);
+ }
+ return {};
+}
+
+mc::MCSymbolRef MachineMCContext::functionSymbol(const MachineFunction &Function) const noexcept {
+ const auto Position = FunctionSymbols.find(&Function);
+ return Position == FunctionSymbols.end() ? mc::MCSymbolRef{} : Position->second;
+}
+
+mc::MCSymbolRef MachineMCContext::globalSymbol(const GlobalObjectDesc &Object) const noexcept {
+ const auto Position = GlobalSymbols.find(&Object);
+ return Position == GlobalSymbols.end() ? mc::MCSymbolRef{} : Position->second;
+}
+
+mc::MCSymbolRef MachineMCContext::blockSymbol(const MachineBasicBlock &Block) const noexcept {
+ const auto Position = BlockSymbols.find(&Block);
+ return Position == BlockSymbols.end() ? mc::MCSymbolRef{} : Position->second;
+}
+
+mc::MCSymbolRef MachineMCContext::constantPoolSymbol(const MachineFunction &Function,
+ ConstantPoolIndex Index) const noexcept {
+ const auto Position =
+ ConstantPoolSymbols.find(FunctionIndexKey{.Function = &Function, .Index = Index.value()});
+ return Position == ConstantPoolSymbols.end() ? mc::MCSymbolRef{} : Position->second;
+}
+
+mc::MCSymbolRef MachineMCContext::jumpTableSymbol(const MachineFunction &Function,
+ JumpTableIndex Index) const noexcept {
+ const auto Position =
+ JumpTableSymbols.find(FunctionIndexKey{.Function = &Function, .Index = Index.value()});
+ return Position == JumpTableSymbols.end() ? mc::MCSymbolRef{} : Position->second;
+}
+
+Expected<mc::MCSymbolRef> MachineMCContext::symbol(std::string Name, mc::MCSymbolKind Kind) {
+ return Context->getOrCreateSymbol(std::move(Name), Kind);
+}
+
+Expected<mc::MCSymbolRef> MachineMCContext::blockAddressSymbol(std::string_view FunctionName,
+ std::string_view BlockName) const {
+ for (const auto &[Block, Symbol] : BlockSymbols) {
+ if (Block == nullptr || Block->function().name() != FunctionName)
+ continue;
+ const auto IRName = Block->attributes().irBlockName();
+ const auto MachineName = Block->attributes().name();
+ const bool NumericMatch =
+ Block->denseNumber() && std::to_string(*Block->denseNumber()) == BlockName;
+ if ((IRName && *IRName == BlockName) || (MachineName && *MachineName == BlockName) ||
+ NumericMatch)
+ return Symbol;
+ }
+ return makeUnexpected(ErrorCode::AssemblyLoweringFailed,
+ "block-address operand does not resolve to an emitted block symbol");
+}
+
+} // namespace rcc
diff --git a/backend/lib/AsmPrinter/TargetMCInstLowering.cpp b/backend/lib/AsmPrinter/TargetMCInstLowering.cpp
new file mode 100644
index 0000000..c171034
--- /dev/null
+++ b/backend/lib/AsmPrinter/TargetMCInstLowering.cpp
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/TargetMCInstLowering.h"
+
+namespace rcc {
+
+Expected<void> TargetMCInstLowering::emitTerminalInstruction(const MachineInstr &,
+ MachineMCContext &,
+ mc::MCStreamer &) const {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "target does not support this terminal emission instruction");
+}
+
+Expected<void> TargetMCInstLowering::emitFileHeader(MachineMCContext &, mc::MCStreamer &) const {
+ return {};
+}
+
+Expected<void> TargetMCInstLowering::emitFileTrailer(MachineMCContext &, mc::MCStreamer &) const {
+ return {};
+}
+
+Expected<void> TargetMCInstLowering::emitGenericConstant(const GenericConstantData &, std::uint64_t,
+ MachineMCContext &,
+ mc::MCStreamer &) const {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "target does not support assembly emission for generic constants");
+}
+
+Expected<void> TargetMCInstLowering::emitGlobalInitializer(const GlobalInitializer &,
+ MachineMCContext &,
+ mc::MCStreamer &) const {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "target does not support assembly emission for global initializers");
+}
+
+Expected<void> TargetMCInstLowering::emitTargetConstant(const TargetConstant &, MachineMCContext &,
+ mc::MCStreamer &) const {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "target does not support assembly emission for this target constant");
+}
+
+Expected<void> TargetMCInstLowering::emitTargetJumpTable(const MachineFunction &,
+ const MachineJumpTableEntry &,
+ MachineMCContext &,
+ mc::MCStreamer &) const {
+ return makeUnexpected(ErrorCode::Unsupported, "target does not support this jump-table encoding");
+}
+
+} // namespace rcc