//===-- llvm-spirv.cpp - The LLVM/SPIR-V translator utility -----*- C++ -*-===// // // // The LLVM/SPIRV Translator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal with the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimers. // Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimers in the documentation // and/or other materials provided with the distribution. // Neither the names of Advanced Micro Devices, Inc., nor the names of its // contributors may be used to endorse or promote products derived from this // Software without specific prior written permission. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH // THE SOFTWARE. // //===----------------------------------------------------------------------===// /// \file /// /// Common Usage: /// llvm-spirv - Read LLVM bitcode from stdin, write SPIRV to stdout /// llvm-spirv x.bc - Read LLVM bitcode from the x.bc file, write SPIR-V /// to x.bil file /// llvm-spirv -r - Read SPIRV from stdin, write LLVM bitcode to stdout /// llvm-spirv -r x.bil - Read SPIRV from the x.bil file, write SPIR-V to /// the x.bc file /// /// Options: /// --help - Output command line options /// //===----------------------------------------------------------------------===// #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DataStream.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Signals.h" #include "llvm/Support/ToolOutputFile.h" #ifndef _SPIRV_SUPPORT_TEXT_FMT #define _SPIRV_SUPPORT_TEXT_FMT #endif #include "llvm/Support/SPIRV.h" #include #include #include #define DEBUG_TYPE "spirv" namespace kExt { const char SpirvBinary[] = ".spv"; const char SpirvText[] = ".spt"; const char LLVMBinary[] = ".bc"; } using namespace llvm; static cl::opt InputFile(cl::Positional, cl::desc(""), cl::init("-")); static cl::opt OutputFile("o", cl::desc("Override output filename"), cl::value_desc("filename")); static cl::opt IsReverse("r", cl::desc("Reverse translation (SPIR-V to LLVM)")); static cl::opt IsRegularization("s", cl::desc( "Regularize LLVM to be representable by SPIR-V")); #ifdef _SPIRV_SUPPORT_TEXT_FMT namespace SPIRV { // Use textual format for SPIRV. extern bool SPIRVUseTextFormat; } static cl::opt ToText("to-text", cl::desc("Convert input SPIR-V binary to internal textual format")); static cl::opt ToBinary("to-binary", cl::desc("Convert input SPIR-V in internal textual format to binary")); #endif static std::string removeExt(const std::string& FileName) { size_t Pos = FileName.find_last_of("."); if (Pos != std::string::npos) return FileName.substr(0, Pos); return FileName; } static int convertLLVMToSPIRV() { LLVMContext Context; std::string Err; auto DS = getDataFileStreamer(InputFile, &Err); if (!DS) { errs() << "Fails to open input file: " << Err; return -1; } ErrorOr> MOrErr = getStreamedBitcodeModule(InputFile, std::move(DS), Context); if (std::error_code EC = MOrErr.getError()) { errs() << "Fails to load bitcode: " << EC.message(); return -1; } std::unique_ptr M = std::move(*MOrErr); if (std::error_code EC = M->materializeAll()){ errs() << "Fails to materialize: " << EC.message(); return -1; } if (OutputFile.empty()) { if (InputFile == "-") OutputFile = "-"; else OutputFile = removeExt(InputFile) + (SPIRV::SPIRVUseTextFormat ? kExt::SpirvText : kExt::SpirvBinary); } llvm::StringRef outFile(OutputFile); std::error_code EC; llvm::raw_fd_ostream OFS(outFile, EC, llvm::sys::fs::F_None); if (!WriteSPIRV(M.get(), OFS, Err)) { errs() << "Fails to save LLVM as SPIRV: " << Err << '\n'; return -1; } return 0; } static int convertSPIRVToLLVM() { LLVMContext Context; std::ifstream IFS(InputFile, std::ios::binary); Module *M; std::string Err; if (!ReadSPIRV(Context, IFS, M, Err)) { errs() << "Fails to load SPIRV as LLVM Module: " << Err << '\n'; return -1; } DEBUG(dbgs() << "Converted LLVM module:\n" << *M); raw_string_ostream ErrorOS(Err); if (verifyModule(*M, &ErrorOS)){ errs() << "Fails to verify module: " << ErrorOS.str(); return -1; } if (OutputFile.empty()) { if (InputFile == "-") OutputFile = "-"; else OutputFile = removeExt(InputFile) + kExt::LLVMBinary; } std::error_code EC; tool_output_file Out(OutputFile.c_str(), EC, sys::fs::F_None); if (EC) { errs() << "Fails to open output file: " << EC.message(); return -1; } WriteBitcodeToFile(M, Out.os()); Out.keep(); delete M; return 0; } #ifdef _SPIRV_SUPPORT_TEXT_FMT static int convertSPIRV() { if (ToBinary == ToText) { errs() << "Invalid arguments\n"; return -1; } std::ifstream IFS(InputFile, std::ios::binary); if (OutputFile.empty()) { if (InputFile == "-") OutputFile = "-"; else { OutputFile = removeExt(InputFile) + (ToBinary?kExt::SpirvBinary:kExt::SpirvText); } } auto Action = [&](llvm::raw_ostream &OFS) { std::string Err; if (!SPIRV::ConvertSPIRV(IFS, OFS, Err, ToBinary, ToText)) { errs() << "Fails to convert SPIR-V : " << Err << '\n'; return -1; } return 0; }; if (OutputFile != "-") { std::error_code EC; llvm::raw_fd_ostream OFS(llvm::StringRef(OutputFile), EC, llvm::sys::fs::F_None); return Action(OFS); } else return Action(outs()); } #endif static int regularizeLLVM() { LLVMContext Context; std::string Err; auto DS = getDataFileStreamer(InputFile, &Err); if (!DS) { errs() << "Fails to open input file: " << Err; return -1; } ErrorOr> MOrErr = getStreamedBitcodeModule(InputFile, std::move(DS), Context); if (std::error_code EC = MOrErr.getError()) { errs() << "Fails to load bitcode: " << EC.message(); return -1; } std::unique_ptr M = std::move(*MOrErr); if (std::error_code EC = M->materializeAll()){ errs() << "Fails to materialize: " << EC.message(); return -1; } if (OutputFile.empty()) { if (InputFile == "-") OutputFile = "-"; else OutputFile = removeExt(InputFile) + ".regularized.bc"; } if (!RegularizeLLVMForSPIRV(M.get(), Err)) { errs() << "Fails to save LLVM as SPIRV: " << Err << '\n'; return -1; } std::error_code EC; tool_output_file Out(OutputFile.c_str(), EC, sys::fs::F_None); if (EC) { errs() << "Fails to open output file: " << EC.message(); return -1; } WriteBitcodeToFile(M.get(), Out.os()); Out.keep(); return 0; } int main(int ac, char** av) { EnablePrettyStackTrace(); sys::PrintStackTraceOnErrorSignal(av[0]); PrettyStackTraceProgram X(ac, av); cl::ParseCommandLineOptions(ac, av, "LLVM/SPIR-V translator"); #ifdef _SPIRV_SUPPORT_TEXT_FMT if (ToText && (ToBinary || IsReverse || IsRegularization)) { errs() << "Cannot use -to-text with -to-binary, -r, -s\n"; return -1; } if (ToBinary && (ToText || IsReverse || IsRegularization)) { errs() << "Cannot use -to-binary with -to-text, -r, -s\n"; return -1; } if (ToBinary || ToText) return convertSPIRV(); #endif if (!IsReverse && !IsRegularization) return convertLLVMToSPIRV(); if (IsReverse && IsRegularization) { errs() << "Cannot have both -r and -s options\n"; return -1; } if (IsReverse) return convertSPIRVToLLVM(); if (IsRegularization) return regularizeLLVM(); return 0; }