Home | History | Annotate | Download | only in llvm-spirv
      1 //===-- llvm-spirv.cpp - The LLVM/SPIR-V translator utility -----*- C++ -*-===//
      2 //
      3 //
      4 //                     The LLVM/SPIRV Translator
      5 //
      6 // This file is distributed under the University of Illinois Open Source
      7 // License. See LICENSE.TXT for details.
      8 //
      9 // Permission is hereby granted, free of charge, to any person obtaining a
     10 // copy of this software and associated documentation files (the "Software"),
     11 // to deal with the Software without restriction, including without limitation
     12 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
     13 // and/or sell copies of the Software, and to permit persons to whom the
     14 // Software is furnished to do so, subject to the following conditions:
     15 //
     16 // Redistributions of source code must retain the above copyright notice,
     17 // this list of conditions and the following disclaimers.
     18 // Redistributions in binary form must reproduce the above copyright notice,
     19 // this list of conditions and the following disclaimers in the documentation
     20 // and/or other materials provided with the distribution.
     21 // Neither the names of Advanced Micro Devices, Inc., nor the names of its
     22 // contributors may be used to endorse or promote products derived from this
     23 // Software without specific prior written permission.
     24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     25 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     27 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     29 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
     30 // THE SOFTWARE.
     31 //
     32 //===----------------------------------------------------------------------===//
     33 /// \file
     34 ///
     35 ///  Common Usage:
     36 ///  llvm-spirv          - Read LLVM bitcode from stdin, write SPIRV to stdout
     37 ///  llvm-spirv x.bc     - Read LLVM bitcode from the x.bc file, write SPIR-V
     38 ///                        to x.bil file
     39 ///  llvm-spirv -r       - Read SPIRV from stdin, write LLVM bitcode to stdout
     40 ///  llvm-spirv -r x.bil - Read SPIRV from the x.bil file, write SPIR-V to
     41 ///                        the x.bc file
     42 ///
     43 ///  Options:
     44 ///      --help   - Output command line options
     45 ///
     46 //===----------------------------------------------------------------------===//
     47 
     48 #include "llvm/Bitcode/ReaderWriter.h"
     49 #include "llvm/IR/LLVMContext.h"
     50 #include "llvm/IR/Module.h"
     51 #include "llvm/IR/Verifier.h"
     52 #include "llvm/Support/CommandLine.h"
     53 #include "llvm/Support/DataStream.h"
     54 #include "llvm/Support/Debug.h"
     55 #include "llvm/Support/FileSystem.h"
     56 #include "llvm/Support/PrettyStackTrace.h"
     57 #include "llvm/Support/raw_ostream.h"
     58 #include "llvm/Support/Signals.h"
     59 #include "llvm/Support/ToolOutputFile.h"
     60 
     61 #ifndef _SPIRV_SUPPORT_TEXT_FMT
     62 #define _SPIRV_SUPPORT_TEXT_FMT
     63 #endif
     64 
     65 #include "llvm/Support/SPIRV.h"
     66 
     67 #include <memory>
     68 #include <fstream>
     69 #include <iostream>
     70 
     71 #define DEBUG_TYPE "spirv"
     72 
     73 namespace kExt {
     74   const char SpirvBinary[] = ".spv";
     75   const char SpirvText[] = ".spt";
     76   const char LLVMBinary[] = ".bc";
     77 }
     78 
     79 using namespace llvm;
     80 
     81 static cl::opt<std::string>
     82 InputFile(cl::Positional, cl::desc("<input file>"), cl::init("-"));
     83 
     84 static cl::opt<std::string>
     85 OutputFile("o", cl::desc("Override output filename"),
     86                cl::value_desc("filename"));
     87 
     88 static cl::opt<bool>
     89 IsReverse("r", cl::desc("Reverse translation (SPIR-V to LLVM)"));
     90 
     91 static cl::opt<bool>
     92 IsRegularization("s", cl::desc(
     93     "Regularize LLVM to be representable by SPIR-V"));
     94 
     95 #ifdef _SPIRV_SUPPORT_TEXT_FMT
     96 namespace SPIRV {
     97 // Use textual format for SPIRV.
     98 extern bool SPIRVUseTextFormat;
     99 }
    100 
    101 static cl::opt<bool>
    102 ToText("to-text", cl::desc("Convert input SPIR-V binary to internal textual format"));
    103 
    104 static cl::opt<bool>
    105 ToBinary("to-binary",
    106     cl::desc("Convert input SPIR-V in internal textual format to binary"));
    107 #endif
    108 
    109 static std::string
    110 removeExt(const std::string& FileName) {
    111   size_t Pos = FileName.find_last_of(".");
    112   if (Pos != std::string::npos)
    113     return FileName.substr(0, Pos);
    114   return FileName;
    115 }
    116 
    117 static int
    118 convertLLVMToSPIRV() {
    119   LLVMContext Context;
    120 
    121   std::string Err;
    122   auto DS = getDataFileStreamer(InputFile, &Err);
    123   if (!DS) {
    124     errs() << "Fails to open input file: " << Err;
    125     return -1;
    126   }
    127 
    128   ErrorOr<std::unique_ptr<Module>> MOrErr =
    129       getStreamedBitcodeModule(InputFile, std::move(DS), Context);
    130 
    131   if (std::error_code EC = MOrErr.getError()) {
    132     errs() << "Fails to load bitcode: " << EC.message();
    133     return -1;
    134   }
    135 
    136   std::unique_ptr<Module> M = std::move(*MOrErr);
    137 
    138   if (std::error_code EC = M->materializeAll()){
    139     errs() << "Fails to materialize: " << EC.message();
    140     return -1;
    141   }
    142 
    143   if (OutputFile.empty()) {
    144     if (InputFile == "-")
    145       OutputFile = "-";
    146     else
    147       OutputFile = removeExt(InputFile) +
    148                    (SPIRV::SPIRVUseTextFormat ? kExt::SpirvText : kExt::SpirvBinary);
    149   }
    150 
    151   llvm::StringRef outFile(OutputFile);
    152   std::error_code EC;
    153   llvm::raw_fd_ostream OFS(outFile, EC, llvm::sys::fs::F_None);
    154   if (!WriteSPIRV(M.get(), OFS, Err)) {
    155     errs() << "Fails to save LLVM as SPIRV: " << Err << '\n';
    156     return -1;
    157   }
    158   return 0;
    159 }
    160 
    161 static int
    162 convertSPIRVToLLVM() {
    163   LLVMContext Context;
    164   std::ifstream IFS(InputFile, std::ios::binary);
    165   Module *M;
    166   std::string Err;
    167 
    168   if (!ReadSPIRV(Context, IFS, M, Err)) {
    169     errs() << "Fails to load SPIRV as LLVM Module: " << Err << '\n';
    170     return -1;
    171   }
    172 
    173   DEBUG(dbgs() << "Converted LLVM module:\n" << *M);
    174 
    175 
    176   raw_string_ostream ErrorOS(Err);
    177   if (verifyModule(*M, &ErrorOS)){
    178     errs() << "Fails to verify module: " << ErrorOS.str();
    179     return -1;
    180   }
    181 
    182   if (OutputFile.empty()) {
    183     if (InputFile == "-")
    184       OutputFile = "-";
    185     else
    186       OutputFile = removeExt(InputFile) + kExt::LLVMBinary;
    187   }
    188 
    189   std::error_code EC;
    190   tool_output_file Out(OutputFile.c_str(), EC, sys::fs::F_None);
    191   if (EC) {
    192     errs() << "Fails to open output file: " << EC.message();
    193     return -1;
    194   }
    195 
    196   WriteBitcodeToFile(M, Out.os());
    197   Out.keep();
    198   delete M;
    199   return 0;
    200 }
    201 
    202 #ifdef _SPIRV_SUPPORT_TEXT_FMT
    203 static int
    204 convertSPIRV() {
    205   if (ToBinary == ToText) {
    206     errs() << "Invalid arguments\n";
    207     return -1;
    208   }
    209   std::ifstream IFS(InputFile, std::ios::binary);
    210 
    211   if (OutputFile.empty()) {
    212     if (InputFile == "-")
    213       OutputFile = "-";
    214     else {
    215       OutputFile = removeExt(InputFile)
    216                  + (ToBinary?kExt::SpirvBinary:kExt::SpirvText);
    217     }
    218   }
    219 
    220   auto Action = [&](llvm::raw_ostream &OFS) {
    221     std::string Err;
    222       if (!SPIRV::ConvertSPIRV(IFS, OFS, Err, ToBinary, ToText)) {
    223       errs() << "Fails to convert SPIR-V : " << Err << '\n';
    224       return -1;
    225     }
    226     return 0;
    227   };
    228   if (OutputFile != "-") {
    229     std::error_code EC;
    230     llvm::raw_fd_ostream OFS(llvm::StringRef(OutputFile), EC, llvm::sys::fs::F_None);
    231     return Action(OFS);
    232   } else
    233     return Action(outs());
    234 }
    235 #endif
    236 
    237 static int
    238 regularizeLLVM() {
    239   LLVMContext Context;
    240 
    241   std::string Err;
    242   auto DS = getDataFileStreamer(InputFile, &Err);
    243   if (!DS) {
    244     errs() << "Fails to open input file: " << Err;
    245     return -1;
    246   }
    247 
    248   ErrorOr<std::unique_ptr<Module>> MOrErr =
    249       getStreamedBitcodeModule(InputFile, std::move(DS), Context);
    250 
    251   if (std::error_code EC = MOrErr.getError()) {
    252     errs() << "Fails to load bitcode: " << EC.message();
    253     return -1;
    254   }
    255 
    256   std::unique_ptr<Module> M = std::move(*MOrErr);
    257 
    258   if (std::error_code EC = M->materializeAll()){
    259     errs() << "Fails to materialize: " << EC.message();
    260     return -1;
    261   }
    262 
    263   if (OutputFile.empty()) {
    264     if (InputFile == "-")
    265       OutputFile = "-";
    266     else
    267       OutputFile = removeExt(InputFile) + ".regularized.bc";
    268   }
    269 
    270   if (!RegularizeLLVMForSPIRV(M.get(), Err)) {
    271     errs() << "Fails to save LLVM as SPIRV: " << Err << '\n';
    272     return -1;
    273   }
    274 
    275   std::error_code EC;
    276   tool_output_file Out(OutputFile.c_str(), EC, sys::fs::F_None);
    277   if (EC) {
    278     errs() << "Fails to open output file: " << EC.message();
    279     return -1;
    280   }
    281 
    282   WriteBitcodeToFile(M.get(), Out.os());
    283   Out.keep();
    284   return 0;
    285 }
    286 
    287 
    288 int
    289 main(int ac, char** av) {
    290   EnablePrettyStackTrace();
    291   sys::PrintStackTraceOnErrorSignal(av[0]);
    292   PrettyStackTraceProgram X(ac, av);
    293 
    294   cl::ParseCommandLineOptions(ac, av, "LLVM/SPIR-V translator");
    295 
    296 #ifdef _SPIRV_SUPPORT_TEXT_FMT
    297   if (ToText && (ToBinary || IsReverse || IsRegularization)) {
    298     errs() << "Cannot use -to-text with -to-binary, -r, -s\n";
    299     return -1;
    300   }
    301 
    302   if (ToBinary && (ToText || IsReverse || IsRegularization)) {
    303     errs() << "Cannot use -to-binary with -to-text, -r, -s\n";
    304     return -1;
    305   }
    306 
    307   if (ToBinary || ToText)
    308     return convertSPIRV();
    309 #endif
    310 
    311   if (!IsReverse && !IsRegularization)
    312     return convertLLVMToSPIRV();
    313 
    314   if (IsReverse && IsRegularization) {
    315     errs() << "Cannot have both -r and -s options\n";
    316     return -1;
    317   }
    318   if (IsReverse)
    319     return convertSPIRVToLLVM();
    320 
    321   if (IsRegularization)
    322     return regularizeLLVM();
    323 
    324   return 0;
    325 }
    326