diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-09 18:31:49 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-09 18:31:49 +0800 |
| commit | 379ff50dcc7ccf977379aee68fa9d10f69065d23 (patch) | |
| tree | e0fac3952d46d0bbcaea32bcfc6087c95fff2992 /backend/tools/AssemblyEmitter | |
| parent | 495c675771f43cfa12ab7a426a538be72fec1805 (diff) | |
| download | rcc-project-379ff50dcc7ccf977379aee68fa9d10f69065d23.tar.gz rcc-project-379ff50dcc7ccf977379aee68fa9d10f69065d23.tar.xz | |
[MC] Implement RV64GC assembly emission
Diffstat (limited to 'backend/tools/AssemblyEmitter')
| -rw-r--r-- | backend/tools/AssemblyEmitter/AssemblyEmitterMain.cpp | 171 | ||||
| -rw-r--r-- | backend/tools/AssemblyEmitter/AssemblyPipelineTest.cmake | 52 | ||||
| -rw-r--r-- | backend/tools/AssemblyEmitter/CMakeLists.txt | 39 |
3 files changed, 262 insertions, 0 deletions
diff --git a/backend/tools/AssemblyEmitter/AssemblyEmitterMain.cpp b/backend/tools/AssemblyEmitter/AssemblyEmitterMain.cpp new file mode 100644 index 0000000..ff339fa --- /dev/null +++ b/backend/tools/AssemblyEmitter/AssemblyEmitterMain.cpp @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineOptimizationPipeline.h" +#include "rcc/CodeGen/MachinePass.h" +#include "rcc/CodeGen/MachinePipelinePasses.h" +#include "rcc/CodeGen/MachineSSAExit.h" +#include "rcc/CodeGen/RegAllocPBQP.h" +#include "rcc/MIR/MIRDocument.h" +#include "rcc/Support/TextDiagnostic.h" +#include "rcc/Target/RISCV/RISCVAssemblyPrinter.h" +#include "rcc/Target/RISCV/RISCVCodeGen.h" +#include "rcc/Target/RISCV/RISCVTarget.h" + +#include <fstream> +#include <iostream> +#include <iterator> +#include <memory> +#include <string> +#include <string_view> +#include <utility> + +namespace { + +struct CommandLine final { + std::string InputPath; +}; + +[[nodiscard]] bool parseCommandLine(int ArgumentCount, char *Arguments[], CommandLine &Result) { + for (int Index = 1; Index < ArgumentCount; ++Index) { + const std::string_view Argument(Arguments[Index]); + if (Argument != "--input" || Index + 1 >= ArgumentCount) { + std::cerr << "usage: rcc-backend-asm --input <mir-file|->\n"; + return false; + } + Result.InputPath = Arguments[++Index]; + } + if (Result.InputPath.empty()) { + std::cerr << "usage: rcc-backend-asm --input <mir-file|->\n"; + return false; + } + return true; +} + +[[nodiscard]] bool readInput(const CommandLine &Command, std::string &Contents) { + if (Command.InputPath == "-") { + Contents.assign(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>()); + return true; + } + std::ifstream Input(Command.InputPath, std::ios::binary); + if (!Input) { + std::cerr << "rcc-backend-asm: cannot open input '" << Command.InputPath << "'\n"; + return false; + } + Contents.assign(std::istreambuf_iterator<char>(Input), std::istreambuf_iterator<char>()); + return true; +} + +template <typename PassT, typename... ArgumentT> +[[nodiscard]] rcc::Expected<void> addPass(rcc::MachinePassManager &Pipeline, + ArgumentT &&...Arguments) { + return Pipeline.addPass(std::make_unique<PassT>(std::forward<ArgumentT>(Arguments)...)); +} + +[[nodiscard]] rcc::Expected<void> runBackendPipeline(rcc::MachineFunction &Function, + const rcc::TargetDescription &Target) { + rcc::MachinePassManager Pipeline; + const rcc::MachineOptimizationPipelineTargetInfo TargetInfo = + rcc::riscv::optimizationPipelineInfo(); + + if (!Function.properties().has(rcc::MachineFunctionProperty::Legalized)) { + auto Added = addPass<rcc::LegalizerPass>(Pipeline, rcc::riscv::legalizerInfo()); + if (!Added) + return std::unexpected(Added.error()); + } + if (!Function.properties().has(rcc::MachineFunctionProperty::RegBankSelected)) { + auto Added = addPass<rcc::RegisterBankSelectPass>(Pipeline, rcc::riscv::registerBankInfo()); + if (!Added) + return std::unexpected(Added.error()); + } + if (!Function.properties().has(rcc::MachineFunctionProperty::Selected)) { + auto Added = addPass<rcc::InstructionSelectPass>(Pipeline, rcc::riscv::instructionSelector()); + if (!Added) + return std::unexpected(Added.error()); + } + if (!Function.properties().has(rcc::MachineFunctionProperty::NoPHIs)) { + auto Added = rcc::addSelectedSSAOptimizationPipeline(Pipeline, TargetInfo); + if (!Added) + return std::unexpected(Added.error()); + Added = addPass<rcc::PHIEliminationPass>(Pipeline, rcc::riscv::ssaInfo()); + if (!Added) + return std::unexpected(Added.error()); + } + if (!Function.properties().has(rcc::MachineFunctionProperty::TiedOpsRewritten)) { + auto Added = addPass<rcc::TwoAddressRewritePass>(Pipeline, rcc::riscv::ssaInfo()); + if (!Added) + return std::unexpected(Added.error()); + } + if (!Function.properties().has(rcc::MachineFunctionProperty::NoVRegs)) { + auto Added = rcc::addPreRegisterAllocationOptimizationPipeline(Pipeline, TargetInfo); + if (!Added) + return std::unexpected(Added.error()); + Added = addPass<rcc::PBQPAllocatorPass>(Pipeline, rcc::riscv::registerAllocationInfo()); + if (!Added) + return std::unexpected(Added.error()); + } + if (!Function.properties().has(rcc::MachineFunctionProperty::NoExecutablePseudos)) { + auto Added = rcc::addPostRegisterAllocationPipeline(Pipeline, TargetInfo); + if (!Added) + return std::unexpected(Added.error()); + } + + rcc::MachineAnalysisManager Analyses; + auto Registered = rcc::registerMachineOptimizationAnalyses(Analyses); + if (!Registered) + return std::unexpected(Registered.error()); + return Pipeline.run(Function, Target, Analyses); +} + +} // namespace + +int main(int ArgumentCount, char *Arguments[]) { + CommandLine Command; + if (!parseCommandLine(ArgumentCount, Arguments, Command)) + return 1; + + std::string SourceText; + if (!readInput(Command, SourceText)) + return 1; + + auto Target = rcc::riscv::createTargetDescription(); + if (!Target) { + std::cerr << "rcc-backend-asm: cannot create RV64GC target: " << Target.error().message() + << '\n'; + return 1; + } + rcc::mir::MIRDocumentParserOptions Options; + Options.DataLayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"; + Options.DefaultPointerSizeInBits = 64; + auto Parsed = + rcc::mir::parseMIRDocument(*Target, Command.InputPath, std::move(SourceText), Options); + if (!Parsed.succeeded()) { + for (const rcc::TextDiagnostic &Diagnostic : Parsed.diagnostics()) + std::cerr << rcc::formatTextDiagnostic(Diagnostic, Parsed.source()) << '\n'; + return 1; + } + + rcc::CompilationUnit &Unit = Parsed.document()->unit(); + for (std::size_t Index = 0; Index < Unit.functionCount(); ++Index) { + rcc::MachineFunction *Function = Unit.functionAt(Index); + if (Function == nullptr) { + std::cerr << "rcc-backend-asm: parsed document contains a null function\n"; + return 1; + } + auto Run = runBackendPipeline(*Function, *Target); + if (!Run) { + std::cerr << "rcc-backend-asm: backend pipeline failed for '" << Function->name() + << "': " << Run.error().message() << '\n'; + return 1; + } + } + + auto Assembly = rcc::riscv::emitAssembly(Unit, *Target); + if (!Assembly) { + std::cerr << "rcc-backend-asm: assembly emission failed: " << Assembly.error().message() + << '\n'; + return 1; + } + std::cout << *Assembly; + return 0; +} diff --git a/backend/tools/AssemblyEmitter/AssemblyPipelineTest.cmake b/backend/tools/AssemblyEmitter/AssemblyPipelineTest.cmake new file mode 100644 index 0000000..baeedcf --- /dev/null +++ b/backend/tools/AssemblyEmitter/AssemblyPipelineTest.cmake @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +if(NOT DEFINED EMITTER OR NOT DEFINED INPUT OR NOT DEFINED OUTPUT) + message(FATAL_ERROR "assembly pipeline test is missing a required path") +endif() + +execute_process( + COMMAND "${EMITTER}" --input "${INPUT}" + RESULT_VARIABLE FirstResult + OUTPUT_VARIABLE FirstAssembly + ERROR_VARIABLE FirstDiagnostics +) +if(NOT FirstResult EQUAL 0) + message(FATAL_ERROR "assembly pipeline failed: ${FirstDiagnostics}") +endif() +if(NOT FirstAssembly MATCHES "\\.attribute arch, \"rv64gc\"" OR + NOT FirstAssembly MATCHES "jalr x0, x1, 0") + message(FATAL_ERROR "assembly pipeline output is missing required RV64GC content") +endif() +if(INPUT MATCHES "RISCVDataPipeline" AND + (NOT FirstAssembly MATCHES "\\.LCPI[0-9]+_0:" OR + NOT FirstAssembly MATCHES "\\.LJTI[0-9]+_0:")) + message(FATAL_ERROR "assembly pipeline did not emit its constant pool and jump table") +endif() + +execute_process( + COMMAND "${EMITTER}" --input "${INPUT}" + RESULT_VARIABLE SecondResult + OUTPUT_VARIABLE SecondAssembly + ERROR_VARIABLE SecondDiagnostics +) +if(NOT SecondResult EQUAL 0) + message(FATAL_ERROR "repeated assembly pipeline failed: ${SecondDiagnostics}") +endif() +if(NOT FirstAssembly STREQUAL SecondAssembly) + message(FATAL_ERROR "assembly pipeline output is not deterministic") +endif() + +if(DEFINED LLVM_MC AND NOT LLVM_MC STREQUAL "LLVM_MC-NOTFOUND" AND + NOT LLVM_MC STREQUAL "") + file(WRITE "${OUTPUT}" "${FirstAssembly}") + execute_process( + COMMAND "${LLVM_MC}" -triple=riscv64-unknown-linux-gnu -filetype=null "${OUTPUT}" + RESULT_VARIABLE AssemblerResult + OUTPUT_VARIABLE AssemblerOutput + ERROR_VARIABLE AssemblerDiagnostics + ) + file(REMOVE "${OUTPUT}") + if(NOT AssemblerResult EQUAL 0) + message(FATAL_ERROR "LLVM MC rejected generated assembly: ${AssemblerDiagnostics}") + endif() +endif() diff --git a/backend/tools/AssemblyEmitter/CMakeLists.txt b/backend/tools/AssemblyEmitter/CMakeLists.txt new file mode 100644 index 0000000..a262983 --- /dev/null +++ b/backend/tools/AssemblyEmitter/CMakeLists.txt @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +add_executable(RCCBackendAssemblyEmitter AssemblyEmitterMain.cpp) +rcc_backend_configure_target(RCCBackendAssemblyEmitter) +target_link_libraries( + RCCBackendAssemblyEmitter + PRIVATE + RCCBackend::MIR + RCCBackend::RISCV +) + +set_target_properties( + RCCBackendAssemblyEmitter + PROPERTIES + OUTPUT_NAME rcc-backend-asm +) + +install( + TARGETS RCCBackendAssemblyEmitter + EXPORT RCCBackendTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" +) + +find_program(RCCBackendLLVMMC NAMES llvm-mc-20 llvm-mc) +foreach(InputName IN ITEMS RISCVGenericPipeline RISCVWideConstantPipeline RISCVDataPipeline) + add_test( + NAME RCCBackend.AssemblyEmitter.${InputName} + COMMAND + "${CMAKE_COMMAND}" + "-DEMITTER=$<TARGET_FILE:RCCBackendAssemblyEmitter>" + "-DINPUT=${RCC_BACKEND_SOURCE_DIR}/unittests/Target/Inputs/${InputName}.mir" + "-DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/${InputName}.s" + "-DLLVM_MC=${RCCBackendLLVMMC}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/AssemblyPipelineTest.cmake" + ) +endforeach() + +rcc_backend_register_format_files("${CMAKE_CURRENT_SOURCE_DIR}/AssemblyEmitterMain.cpp") +rcc_backend_register_tidy_files("${CMAKE_CURRENT_SOURCE_DIR}/AssemblyEmitterMain.cpp") |
