This commit is contained in:
kim 2025-12-19 21:07:20 +00:00 committed by GitHub
commit d523862608
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 316 additions and 0 deletions

39
CMakeLists.txt Normal file
View File

@ -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
)

55
README.md Normal file
View File

@ -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
```
<h2>Options</h2>
<ul>
<li>
<strong>Input file</strong><br>
<code>&lt;input file&gt;</code><br>
<span style="margin-left:2em;">Input SPIR-V binary file (use <code>-</code> for stdin)</span>
</li>
<li>
<strong>Output file</strong><br>
<code>-o &lt;output file&gt;</code><br>
<span style="margin-left:2em;">Specify output file (default: stdout)</span>
</li>
<li>
<strong>SPIR-V MLIR Dump</strong><br>
<code>--dump-mlir</code><br>
<span style="margin-left:2em;">Dump the SPIR-V MLIR dialect</span>
</li>
<li>
<strong>Full Dump</strong><br>
<code>--dump-all</code><br>
<span style="margin-left:2em;">Dump all dialects and LLVM IR</span>
</li>
</ul>

25
include/LumeLib.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef LUMELIB_H
#define LUMELIB_H
#include "mlir/IR/MLIRContext.h"
#include "llvm/Support/LogicalResult.h"
#include <llvm/Support/MemoryBuffer.h>
#include <mlir/Dialect/SPIRV/IR/SPIRVOps.h>
#include <mlir/Dialect/SPIRV/IR/SPIRVDialect.h>
#include <mlir/Target/SPIRV/Deserialization.h>
namespace lume {
mlir::OwningOpRef<mlir::spirv::ModuleOp>
CompileSPVToMLIR(std::unique_ptr<llvm::MemoryBuffer> input,
mlir::MLIRContext &context,
mlir::spirv::DeserializationOptions &options);
llvm::LogicalResult ConvertSPVDialectToLLVMDialect(
mlir::MLIRContext &context,
mlir::ModuleOp *module);
}
#endif

19
lib/CMakeLists.txt Normal file
View File

@ -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
)

55
lib/LumeLib.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "LumeLib.h"
#include <llvm/ADT/ArrayRef.h>
#include <llvm/Support/LogicalResult.h>
#include <mlir/Conversion/SPIRVToLLVM/SPIRVToLLVMPass.h>
#include <mlir/Dialect/SPIRV/IR/SPIRVEnums.h>
#include <mlir/IR/MLIRContext.h>
#include <mlir/Pass/PassManager.h>
#include <cstdint>
#include <memory>
namespace lume {
mlir::OwningOpRef<mlir::spirv::ModuleOp>
CompileSPVToMLIR(std::unique_ptr<llvm::MemoryBuffer> 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<const uint32_t *>(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);
}
}

102
main.cpp Normal file
View File

@ -0,0 +1,102 @@
#include "LumeLib.h"
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/InitLLVM.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/ToolOutputFile.h>
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
#include <mlir/Dialect/SPIRV/IR/SPIRVDialect.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/MLIRContext.h>
#include <mlir/InitAllTranslations.h>
#include <mlir/Support/FileUtilities.h>
#include <mlir/Target/SPIRV/Deserialization.h>
#include <memory>
#include <string>
namespace {
llvm::cl::opt<std::string> inputFilename(
llvm::cl::Positional,
llvm::cl::desc("<input file>"),
llvm::cl::init("-"));
llvm::cl::opt<std::string> outputFilename(
"o",
llvm::cl::desc("Output file (default: stdout)"),
llvm::cl::value_desc("filename"),
llvm::cl::init("-"));
llvm::cl::opt<bool> dumpMLIR(
"dump-mlir",
llvm::cl::desc("Dump the SPIR-V MLIR dialect"),
llvm::cl::init(false));
llvm::cl::opt<bool> 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<mlir::spirv::SPIRVDialect>();
context.loadDialect<mlir::LLVM::LLVMDialect>();
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;
}

21
test/SPIRV/add.spv.asm Normal file
View File

@ -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