Skip to content
Merged
1 change: 1 addition & 0 deletions llvm/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ set(LLVM_TEST_DEPENDS
opt
sancov
sanstats
spirv-to-ir-wrapper
sycl-post-link
split-file
verify-uselistorder
Expand Down
49 changes: 49 additions & 0 deletions llvm/test/tools/spirv-to-ir-wrapper/spirv-to-ir-wrapper.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
; Check for passthrough abilities
; RUN: llvm-as %s -o %t.bc
; RUN: spirv-to-ir-wrapper %t.bc -o %t_1.bc
; RUN: llvm-dis %t_1.bc -o %t_1.ll
; RUN: FileCheck %s --input-file %t_1.ll

; Check for SPIR-V conversion
; RUN: llvm-spirv %t.bc -o %t.spv
; RUN: spirv-to-ir-wrapper %t.spv -o %t_2.bc
; RUN: llvm-dis %t_2.bc -o %t_2.ll
; RUN: FileCheck %s --input-file %t_2.ll

; CHECK: target datalayout
; CHECK-NEXT: target triple = "spir-unknown-unknown"
; CHECK: Function Attrs: nounwind
; CHECK-NEXT: define spir_kernel void @foo(i32 addrspace(1)* %a)
; CHECK-NEXT: entry:
; CHECK-NEXT: %a.addr = alloca i32 addrspace(1)*, align 4
; CHECK-NEXT: store i32 addrspace(1)* %a, i32 addrspace(1)** %a.addr, align 4
; CHECK-NEXT: ret void

target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
target triple = "spir-unknown-unknown"

; Function Attrs: nounwind
define spir_kernel void @foo(i32 addrspace(1)* %a) #0 !kernel_arg_addr_space !1 !kernel_arg_access_qual !2 !kernel_arg_type !3 !kernel_arg_base_type !4 !kernel_arg_type_qual !5 {
entry:
%a.addr = alloca i32 addrspace(1)*, align 4
store i32 addrspace(1)* %a, i32 addrspace(1)** %a.addr, align 4
ret void
}

attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!opencl.enable.FP_CONTRACT = !{}
!opencl.spir.version = !{!6}
!opencl.ocl.version = !{!6}
!opencl.used.extensions = !{!7}
!opencl.used.optional.core.features = !{!7}
!opencl.compiler.options = !{!7}

!1 = !{i32 1}
!2 = !{!"none"}
!3 = !{!"int*"}
!4 = !{!"int*"}
!5 = !{!""}
!6 = !{i32 1, i32 2}
!7 = !{}

13 changes: 13 additions & 0 deletions llvm/tools/spirv-to-ir-wrapper/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set(LLVM_LINK_COMPONENTS
Core
Support
SPIRVLib
)

include_directories(
${LLVM_EXTERNAL_LLVM_SPIRV_SOURCE_DIR}/include
)

add_llvm_tool(spirv-to-ir-wrapper
spirv-to-ir-wrapper.cpp
)
138 changes: 138 additions & 0 deletions llvm/tools/spirv-to-ir-wrapper/spirv-to-ir-wrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//===--- spirv-to-ir-wrapper.cpp - Utility to convert to ir if needed -----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This utility checks if the input file is SPIR-V based. If so, convert to IR
// The input can be either SPIR-V or LLVM-IR. When LLVM-IR, copy the file to
// the specified output.
//
// Uses llvm-spirv to perform the conversion if needed.
//
// The output file is used to allow for proper input and output flow within
// the driver toolchain.
//
// Usage: spirv-to-ir-wrapper input.spv -o output.bc
//
//===----------------------------------------------------------------------===//

#include "LLVMSPIRVLib.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/StringSaver.h"

using namespace llvm;

// InputFilename - The filename to read from.
static cl::opt<std::string> InputFilename(cl::Positional,
cl::value_desc("<input spv file>"),
cl::desc("<input file>"));

// Output - The filename to output to.
static cl::opt<std::string> Output("o", cl::value_desc("output IR filename"),
cl::desc("output filename"));

// LlvmSpirvOpts - The filename to output to.
static cl::opt<std::string>
LlvmSpirvOpts("llvm-spirv-opts", cl::value_desc("llvm-spirv options"),
cl::desc("options to pass to llvm-spirv"));

static void error(const Twine &Message) {
llvm::errs() << "spirv-to-ir-wrapper: " << Message << '\n';
exit(1);
}

// Convert the SPIR-V to LLVM-IR.
static int convertSPIRVToLLVMIR(const char *Argv0) {
// Find llvm-spirv. It is expected this resides in the same directory
// as spirv-to-ir-wrapper.
StringRef ParentPath = llvm::sys::path::parent_path(Argv0);
llvm::ErrorOr<std::string> LlvmSpirvBinary =
llvm::sys::findProgramByName("llvm-spirv", ParentPath);
if (!LlvmSpirvBinary)
LlvmSpirvBinary = llvm::sys::findProgramByName("llvm-spirv");

SmallVector<StringRef, 6> LlvmSpirvArgs = {"llvm-spirv", "-r", InputFilename,
"-o", Output};

// Add any additional options specified by the user.
SmallVector<const char *, 8> TargetArgs;
llvm::BumpPtrAllocator BPA;
llvm::StringSaver S(BPA);
if (!LlvmSpirvOpts.empty()) {
// Tokenize the string.
llvm::cl::TokenizeGNUCommandLine(LlvmSpirvOpts, S, TargetArgs);
std::copy(TargetArgs.begin(), TargetArgs.end(),
std::back_inserter(LlvmSpirvArgs));
}

return llvm::sys::ExecuteAndWait(LlvmSpirvBinary.get(), LlvmSpirvArgs);
}

static int copyInputToOutput() {
return llvm::sys::fs::copy_file(InputFilename, Output).value();
}

static bool isSPIRVBinary(const std::string &File) {
auto FileOrError = MemoryBuffer::getFile(File, /*IsText=*/false,
/*RequiresNullTerminator=*/false);
if (!FileOrError)
return false;
std::unique_ptr<MemoryBuffer> FileBuffer = std::move(*FileOrError);
return SPIRV::isSpirvBinary(FileBuffer->getBuffer().str());
}

static bool isLLVMIRBinary(const std::string &File) {
if (File.size() < sizeof(unsigned))
return false;

StringRef Ext = llvm::sys::path::has_extension(File)
? llvm::sys::path::extension(File).drop_front()
: "";
llvm::file_magic Magic;
llvm::identify_magic(File, Magic);

// Only .bc and bitcode files are to be considered.
return (Ext == "bc" || Magic == llvm::file_magic::bitcode);
}

static int checkInputFileIsAlreadyLLVM(const char *Argv0) {
StringRef Ext = llvm::sys::path::has_extension(InputFilename)
? llvm::sys::path::extension(InputFilename).drop_front()
: "";
if (Ext == "bc" || isLLVMIRBinary(InputFilename))
return copyInputToOutput();
if (Ext == "spv" || isSPIRVBinary(InputFilename))
return convertSPIRVToLLVMIR(Argv0);

// We could not directly determine the input file, so we just copy it
// to the output file.
return copyInputToOutput();
}

int main(int argc, char **argv) {
InitLLVM X(argc, argv);

LLVMContext Context;
cl::ParseCommandLineOptions(argc, argv, "spirv-to-ir-wrapper\n");

if (InputFilename.empty())
error("No input file provided");

if (!llvm::sys::fs::exists(InputFilename))
error("Input file \'" + InputFilename + "\' not found");

if (Output.empty())
error("Output file not provided");

return checkInputFileIsAlreadyLLVM(argv[0]);
}
2 changes: 2 additions & 0 deletions sycl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ add_custom_target(sycl-compiler
llvm-spirv
llvm-link
llvm-objcopy
spirv-to-ir-wrapper
sycl-post-link
opencl-aot
)
Expand Down Expand Up @@ -292,6 +293,7 @@ set( SYCL_TOOLCHAIN_DEPLOY_COMPONENTS
llvm-spirv
llvm-link
llvm-objcopy
spirv-to-ir-wrapper
sycl-post-link
sycl-ls
clang-resource-headers
Expand Down