aboutsummaryrefslogtreecommitdiff
path: root/backend/tools
diff options
context:
space:
mode:
Diffstat (limited to 'backend/tools')
-rw-r--r--backend/tools/AssemblyEmitter/AssemblyEmitterMain.cpp78
-rw-r--r--backend/tools/AssemblyEmitter/AssemblyPipelineTest.cmake5
-rw-r--r--backend/tools/AssemblyEmitter/CMakeLists.txt22
-rw-r--r--backend/tools/AssemblyEmitter/ObjectPipelineTest.cmake88
4 files changed, 186 insertions, 7 deletions
diff --git a/backend/tools/AssemblyEmitter/AssemblyEmitterMain.cpp b/backend/tools/AssemblyEmitter/AssemblyEmitterMain.cpp
index ff339fa..8697754 100644
--- a/backend/tools/AssemblyEmitter/AssemblyEmitterMain.cpp
+++ b/backend/tools/AssemblyEmitter/AssemblyEmitterMain.cpp
@@ -10,8 +10,10 @@
#include "rcc/Support/TextDiagnostic.h"
#include "rcc/Target/RISCV/RISCVAssemblyPrinter.h"
#include "rcc/Target/RISCV/RISCVCodeGen.h"
+#include "rcc/Target/RISCV/RISCVObjectFile.h"
#include "rcc/Target/RISCV/RISCVTarget.h"
+#include <cstdint>
#include <fstream>
#include <iostream>
#include <iterator>
@@ -19,29 +21,86 @@
#include <string>
#include <string_view>
#include <utility>
+#include <vector>
namespace {
+enum class OutputKind : std::uint8_t {
+ Assembly,
+ Object,
+};
+
struct CommandLine final {
std::string InputPath;
+ std::string OutputPath = "-";
+ OutputKind Kind = OutputKind::Assembly;
};
+void printUsage() {
+ std::cerr << "usage: rcc-backend-asm --input <mir-file|-> "
+ "[--filetype asm|obj] [--output <file|->]\n";
+}
+
[[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";
+ if (Argument == "--input" && Index + 1 < ArgumentCount) {
+ Result.InputPath = Arguments[++Index];
+ continue;
+ }
+ if ((Argument == "--output" || Argument == "-o") && Index + 1 < ArgumentCount) {
+ Result.OutputPath = Arguments[++Index];
+ continue;
+ }
+ if (Argument == "--filetype" && Index + 1 < ArgumentCount) {
+ const std::string_view Value(Arguments[++Index]);
+ if (Value == "asm" || Value == "assembly") {
+ Result.Kind = OutputKind::Assembly;
+ } else if (Value == "obj" || Value == "object") {
+ Result.Kind = OutputKind::Object;
+ } else {
+ printUsage();
+ return false;
+ }
+ continue;
+ }
+ {
+ printUsage();
return false;
}
- Result.InputPath = Arguments[++Index];
}
if (Result.InputPath.empty()) {
- std::cerr << "usage: rcc-backend-asm --input <mir-file|->\n";
+ printUsage();
+ return false;
+ }
+ return true;
+}
+
+[[nodiscard]] bool writeOutput(const CommandLine &Command, std::span<const std::uint8_t> Contents) {
+ if (Command.OutputPath == "-") {
+ std::cout.write(reinterpret_cast<const char *>(Contents.data()),
+ static_cast<std::streamsize>(Contents.size()));
+ return static_cast<bool>(std::cout);
+ }
+ std::ofstream Output(Command.OutputPath, std::ios::binary);
+ if (!Output) {
+ std::cerr << "rcc-backend-asm: cannot open output '" << Command.OutputPath << "'\n";
+ return false;
+ }
+ Output.write(reinterpret_cast<const char *>(Contents.data()),
+ static_cast<std::streamsize>(Contents.size()));
+ if (!Output) {
+ std::cerr << "rcc-backend-asm: cannot write output '" << Command.OutputPath << "'\n";
return false;
}
return true;
}
+[[nodiscard]] bool writeOutput(const CommandLine &Command, std::string_view Contents) {
+ const auto *Data = reinterpret_cast<const std::uint8_t *>(Contents.data());
+ return writeOutput(Command, std::span<const std::uint8_t>(Data, Contents.size()));
+}
+
[[nodiscard]] bool readInput(const CommandLine &Command, std::string &Contents) {
if (Command.InputPath == "-") {
Contents.assign(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>());
@@ -160,12 +219,19 @@ int main(int ArgumentCount, char *Arguments[]) {
}
}
+ if (Command.Kind == OutputKind::Object) {
+ auto Object = rcc::riscv::emitObject(Unit, *Target);
+ if (!Object) {
+ std::cerr << "rcc-backend-asm: object emission failed: " << Object.error().message() << '\n';
+ return 1;
+ }
+ return writeOutput(Command, *Object) ? 0 : 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;
+ return writeOutput(Command, *Assembly) ? 0 : 1;
}
diff --git a/backend/tools/AssemblyEmitter/AssemblyPipelineTest.cmake b/backend/tools/AssemblyEmitter/AssemblyPipelineTest.cmake
index baeedcf..85977df 100644
--- a/backend/tools/AssemblyEmitter/AssemblyPipelineTest.cmake
+++ b/backend/tools/AssemblyEmitter/AssemblyPipelineTest.cmake
@@ -22,6 +22,11 @@ if(INPUT MATCHES "RISCVDataPipeline" AND
NOT FirstAssembly MATCHES "\\.LJTI[0-9]+_0:"))
message(FATAL_ERROR "assembly pipeline did not emit its constant pool and jump table")
endif()
+if(INPUT MATCHES "RISCVStackFramePipeline" AND
+ (NOT FirstAssembly MATCHES "addi x2, x2, -32" OR
+ NOT FirstAssembly MATCHES "addi x2, x2, 32"))
+ message(FATAL_ERROR "assembly pipeline did not balance its stack frame")
+endif()
execute_process(
COMMAND "${EMITTER}" --input "${INPUT}"
diff --git a/backend/tools/AssemblyEmitter/CMakeLists.txt b/backend/tools/AssemblyEmitter/CMakeLists.txt
index a262983..01c99aa 100644
--- a/backend/tools/AssemblyEmitter/CMakeLists.txt
+++ b/backend/tools/AssemblyEmitter/CMakeLists.txt
@@ -22,7 +22,16 @@ install(
)
find_program(RCCBackendLLVMMC NAMES llvm-mc-20 llvm-mc)
-foreach(InputName IN ITEMS RISCVGenericPipeline RISCVWideConstantPipeline RISCVDataPipeline)
+find_program(RCCBackendLLVMReadELF NAMES llvm-readelf-20 llvm-readelf)
+find_program(RCCBackendLLVMObjdump NAMES llvm-objdump-20 llvm-objdump)
+foreach(
+ InputName
+ IN ITEMS
+ RISCVGenericPipeline
+ RISCVWideConstantPipeline
+ RISCVDataPipeline
+ RISCVStackFramePipeline
+)
add_test(
NAME RCCBackend.AssemblyEmitter.${InputName}
COMMAND
@@ -33,6 +42,17 @@ foreach(InputName IN ITEMS RISCVGenericPipeline RISCVWideConstantPipeline RISCVD
"-DLLVM_MC=${RCCBackendLLVMMC}"
-P "${CMAKE_CURRENT_SOURCE_DIR}/AssemblyPipelineTest.cmake"
)
+ add_test(
+ NAME RCCBackend.ObjectEmitter.${InputName}
+ COMMAND
+ "${CMAKE_COMMAND}"
+ "-DEMITTER=$<TARGET_FILE:RCCBackendAssemblyEmitter>"
+ "-DINPUT=${RCC_BACKEND_SOURCE_DIR}/unittests/Target/Inputs/${InputName}.mir"
+ "-DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/${InputName}.o"
+ "-DLLVM_READELF=${RCCBackendLLVMReadELF}"
+ "-DLLVM_OBJDUMP=${RCCBackendLLVMObjdump}"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/ObjectPipelineTest.cmake"
+ )
endforeach()
rcc_backend_register_format_files("${CMAKE_CURRENT_SOURCE_DIR}/AssemblyEmitterMain.cpp")
diff --git a/backend/tools/AssemblyEmitter/ObjectPipelineTest.cmake b/backend/tools/AssemblyEmitter/ObjectPipelineTest.cmake
new file mode 100644
index 0000000..5ba800c
--- /dev/null
+++ b/backend/tools/AssemblyEmitter/ObjectPipelineTest.cmake
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+if(NOT DEFINED EMITTER OR NOT DEFINED INPUT OR NOT DEFINED OUTPUT)
+ message(FATAL_ERROR "object pipeline test is missing a required path")
+endif()
+
+set(RepeatedOutput "${OUTPUT}.repeat")
+execute_process(
+ COMMAND "${EMITTER}" --input "${INPUT}" --filetype obj --output "${OUTPUT}"
+ RESULT_VARIABLE FirstResult
+ ERROR_VARIABLE FirstDiagnostics
+)
+if(NOT FirstResult EQUAL 0)
+ message(FATAL_ERROR "object pipeline failed: ${FirstDiagnostics}")
+endif()
+
+execute_process(
+ COMMAND "${EMITTER}" --input "${INPUT}" --filetype obj --output "${RepeatedOutput}"
+ RESULT_VARIABLE SecondResult
+ ERROR_VARIABLE SecondDiagnostics
+)
+if(NOT SecondResult EQUAL 0)
+ message(FATAL_ERROR "repeated object pipeline failed: ${SecondDiagnostics}")
+endif()
+
+execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E compare_files "${OUTPUT}" "${RepeatedOutput}"
+ RESULT_VARIABLE ComparisonResult
+)
+if(NOT ComparisonResult EQUAL 0)
+ message(FATAL_ERROR "object pipeline output is not deterministic")
+endif()
+
+file(READ "${OUTPUT}" ObjectMagic OFFSET 0 LIMIT 4 HEX)
+if(NOT ObjectMagic STREQUAL "7f454c46")
+ message(FATAL_ERROR "object pipeline did not produce an ELF file")
+endif()
+
+if(DEFINED LLVM_READELF AND NOT LLVM_READELF STREQUAL "LLVM_READELF-NOTFOUND" AND
+ NOT LLVM_READELF STREQUAL "")
+ execute_process(
+ COMMAND "${LLVM_READELF}" -h -S -s -r "${OUTPUT}"
+ RESULT_VARIABLE ReadELFResult
+ OUTPUT_VARIABLE ReadELFOutput
+ ERROR_VARIABLE ReadELFDiagnostics
+ )
+ if(NOT ReadELFResult EQUAL 0)
+ message(FATAL_ERROR "LLVM readelf rejected generated object: ${ReadELFDiagnostics}")
+ endif()
+ if(NOT ReadELFOutput MATCHES "Type:[ \t]+REL" OR
+ NOT ReadELFOutput MATCHES "Machine:[ \t]+RISC-V" OR
+ NOT ReadELFOutput MATCHES "Flags:[ \t]+0x5" OR
+ NOT ReadELFOutput MATCHES "\\.text" OR
+ NOT ReadELFOutput MATCHES "\\.eh_frame" OR
+ NOT ReadELFOutput MATCHES "\\.riscv\\.attributes")
+ message(FATAL_ERROR "generated object is missing required RV64GC ELF metadata")
+ endif()
+ if(INPUT MATCHES "RISCVDataPipeline" AND
+ (NOT ReadELFOutput MATCHES "\\.rodata" OR
+ NOT ReadELFOutput MATCHES "\\.rela\\.text" OR
+ NOT ReadELFOutput MATCHES "R_RISCV_64"))
+ message(FATAL_ERROR "data pipeline object is missing data or relocation sections")
+ endif()
+endif()
+
+if(DEFINED LLVM_OBJDUMP AND NOT LLVM_OBJDUMP STREQUAL "LLVM_OBJDUMP-NOTFOUND" AND
+ NOT LLVM_OBJDUMP STREQUAL "")
+ execute_process(
+ COMMAND "${LLVM_OBJDUMP}" -dr --no-show-raw-insn "${OUTPUT}"
+ RESULT_VARIABLE ObjdumpResult
+ OUTPUT_VARIABLE ObjdumpOutput
+ ERROR_VARIABLE ObjdumpDiagnostics
+ )
+ if(NOT ObjdumpResult EQUAL 0)
+ message(FATAL_ERROR "LLVM objdump rejected generated object: ${ObjdumpDiagnostics}")
+ endif()
+ if(NOT ObjdumpOutput MATCHES "file format elf64-littleriscv" OR
+ NOT ObjdumpOutput MATCHES "ret")
+ message(FATAL_ERROR "generated object does not contain RV64GC function code")
+ endif()
+ if(INPUT MATCHES "RISCVStackFramePipeline" AND
+ (NOT ObjdumpOutput MATCHES "addi[ \t]+sp, sp, -0x20" OR
+ NOT ObjdumpOutput MATCHES "addi[ \t]+sp, sp, 0x20"))
+ message(FATAL_ERROR "stack-frame object does not balance its stack pointer")
+ endif()
+endif()
+
+file(REMOVE "${OUTPUT}" "${RepeatedOutput}")