commit d523862608bcfc4c53966282c37a87ceb7934e83 Author: kim Date: Fri Dec 19 21:07:20 2025 +0000 Initial diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..17a64c7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.20) +project(LUME LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(LLVM_DIR "" CACHE PATH "Path to the LLVM build cmake directory") +set(MLIR_DIR "" CACHE PATH "Path to the MLIR build cmake directory") + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +find_package(LLVM REQUIRED CONFIG) +find_package(MLIR REQUIRED CONFIG) + +include(AddLLVM) +include(TableGen) + +add_subdirectory(lib) + +add_executable(lume + main.cpp +) + +target_link_libraries(lume PRIVATE + LUMELIB + LLVMCore + LLVMSupport + MLIRSupport + MLIRSPIRVTranslateRegistration + MLIRSPIRVBinaryUtils + MLIRLLVMDialect +) + +target_include_directories(lume PRIVATE + ${LLVM_INCLUDE_DIRS} + ${MLIR_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/include +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..d07625b --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# lume + +lume is a compiler that converts SPIR-V binaries to MLIR and then to LLVM IR. This enables cross compilation of SPIR-V shaders to various target architectures through the LLVM toolchain + +## Building + +### Prerequisites + +- CMake 3.20 or higher +- LLVM (w/ MLIR support) +- C++17 compatible compiler + +### Build + +```bash +mkdir build +cd build +cmake .. -DLLVM_DIR=/path/to/llvm/build/lib/cmake/llvm \ + -DMLIR_DIR=/path/to/mlir/build/lib/cmake/mlir +make +``` + +## Usage + +```bash +lume input.spv -o output.mlir +lume input.spv --dump-mlir +lume input.spv --dump-all +lume - -o output.mlir +``` + +

Options

+ + diff --git a/include/LumeLib.h b/include/LumeLib.h new file mode 100644 index 0000000..5406a26 --- /dev/null +++ b/include/LumeLib.h @@ -0,0 +1,25 @@ +#ifndef LUMELIB_H +#define LUMELIB_H + +#include "mlir/IR/MLIRContext.h" +#include "llvm/Support/LogicalResult.h" +#include +#include +#include +#include + +namespace lume { + +mlir::OwningOpRef +CompileSPVToMLIR(std::unique_ptr input, + mlir::MLIRContext &context, + mlir::spirv::DeserializationOptions &options); + +llvm::LogicalResult ConvertSPVDialectToLLVMDialect( + mlir::MLIRContext &context, + mlir::ModuleOp *module); + +} + +#endif + diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..6c1a343 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,19 @@ +add_library(LUMELIB STATIC + LumeLib.cpp +) + +target_link_libraries(LUMELIB PRIVATE + LLVMCore + LLVMSupport + MLIRSupport + MLIRSPIRVTranslateRegistration + MLIRSPIRVBinaryUtils + MLIRPass + MLIRSPIRVToLLVM +) + +target_include_directories(LUMELIB PRIVATE + ${LLVM_INCLUDE_DIRS} + ${MLIR_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/include +) diff --git a/lib/LumeLib.cpp b/lib/LumeLib.cpp new file mode 100644 index 0000000..84f499a --- /dev/null +++ b/lib/LumeLib.cpp @@ -0,0 +1,55 @@ +#include "LumeLib.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace lume { + +mlir::OwningOpRef +CompileSPVToMLIR(std::unique_ptr input, + mlir::MLIRContext &context, + mlir::spirv::DeserializationOptions &options) { + if (!input) { + return nullptr; + } + + const auto *start = input->getBufferStart(); + const auto size = input->getBufferSize(); + + if (size % sizeof(uint32_t) != 0) { + return nullptr; + } + + const auto binary = llvm::ArrayRef( + reinterpret_cast(start), + size / sizeof(uint32_t)); + + return mlir::spirv::deserialize(binary, &context, options); +} + +llvm::LogicalResult ConvertSPVDialectToLLVMDialect( + mlir::MLIRContext &context, + mlir::ModuleOp *module) { + if (!module) { + return llvm::failure(); + } + + mlir::ConvertSPIRVToLLVMPassOptions options; + options.clientAPI = mlir::spirv::ClientAPI::Vulkan; + + mlir::PassManager pm(&context); + pm.addPass(mlir::createConvertSPIRVToLLVMPass(options)); + + return pm.run(*module); +} + +} + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..24bdfed --- /dev/null +++ b/main.cpp @@ -0,0 +1,102 @@ +#include "LumeLib.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { +llvm::cl::opt inputFilename( + llvm::cl::Positional, + llvm::cl::desc(""), + llvm::cl::init("-")); + +llvm::cl::opt outputFilename( + "o", + llvm::cl::desc("Output file (default: stdout)"), + llvm::cl::value_desc("filename"), + llvm::cl::init("-")); + +llvm::cl::opt dumpMLIR( + "dump-mlir", + llvm::cl::desc("Dump the SPIR-V MLIR dialect"), + llvm::cl::init(false)); + +llvm::cl::opt dumpAll( + "dump-all", + llvm::cl::desc("Dump all dialects and LLVM IR"), + llvm::cl::init(false)); +} + +int main(int argc, char **argv) { + llvm::InitLLVM initLLVM(argc, argv); + llvm::cl::ParseCommandLineOptions(argc, argv, "Lume Compiler - SPIR-V to LLVM"); + + mlir::registerFromSPIRVTranslation(); + + std::string errorMessage; + auto input = mlir::openInputFile(inputFilename, &errorMessage); + if (!input) { + llvm::errs() << "Error: " << errorMessage << '\n'; + return 1; + } + + mlir::MLIRContext context; + context.loadDialect(); + context.loadDialect(); + + mlir::spirv::DeserializationOptions options; + auto spvModule = lume::CompileSPVToMLIR(std::move(input), context, options); + + if (!spvModule) { + llvm::errs() << "Error: Failed to deserialize SPIR-V binary\n"; + return 1; + } + + mlir::OpBuilder builder(&context); + auto topModule = mlir::ModuleOp::create(builder.getUnknownLoc()); + topModule.getBody()->push_back(spvModule.release()); + + if (dumpMLIR || dumpAll) { + llvm::errs() << "=== MLIR SPIR-V Dialect ===\n"; + topModule.dump(); + llvm::errs() << "\n"; + } + + if (llvm::failed(lume::ConvertSPVDialectToLLVMDialect(context, &topModule))) { + llvm::errs() << "Error: Failed to convert SPIR-V dialect to LLVM dialect\n"; + return 1; + } + + if (dumpMLIR || dumpAll) { + llvm::errs() << "=== MLIR LLVM Dialect ===\n"; + topModule.dump(); + llvm::errs() << "\n"; + } + + if (outputFilename != "-") { + auto output = mlir::openOutputFile(outputFilename, &errorMessage); + if (!output) { + llvm::errs() << "Error: " << errorMessage << '\n'; + return 1; + } + + topModule.print(output->os()); + output->keep(); + } else if (!dumpMLIR && !dumpAll) { + topModule.print(llvm::outs()); + } + + return 0; +} diff --git a/test/SPIRV/add.spv.asm b/test/SPIRV/add.spv.asm new file mode 100644 index 0000000..e496389 --- /dev/null +++ b/test/SPIRV/add.spv.asm @@ -0,0 +1,21 @@ +; SPIR-V +; Version: 1.0 +; Generator: 0 +; Bound: 8 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + +; int add(int a, int b) { return a + b; } + + %1 = OpTypeInt 32 1 ; i32 (signed) + %2 = OpTypeFunction %1 %1 %1 ; return i32, params (i32, i32) + + %3 = OpFunction %1 None %2 + %4 = OpFunctionParameter %1 ; param a + %5 = OpFunctionParameter %1 ; param b + %6 = OpLabel + %7 = OpIAdd %1 %4 %5 ; a + b + OpReturnValue %7 + OpFunctionEnd +