Initial
This commit is contained in:
commit
d523862608
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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><input file></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 <output file></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>
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
Loading…
Reference in New Issue