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
73namespace kExt {
74  const char SpirvBinary[] = ".spv";
75  const char SpirvText[] = ".spt";
76  const char LLVMBinary[] = ".bc";
77}
78
79using namespace llvm;
80
81static cl::opt<std::string>
82InputFile(cl::Positional, cl::desc("<input file>"), cl::init("-"));
83
84static cl::opt<std::string>
85OutputFile("o", cl::desc("Override output filename"),
86               cl::value_desc("filename"));
87
88static cl::opt<bool>
89IsReverse("r", cl::desc("Reverse translation (SPIR-V to LLVM)"));
90
91static cl::opt<bool>
92IsRegularization("s", cl::desc(
93    "Regularize LLVM to be representable by SPIR-V"));
94
95#ifdef _SPIRV_SUPPORT_TEXT_FMT
96namespace SPIRV {
97// Use textual format for SPIRV.
98extern bool SPIRVUseTextFormat;
99}
100
101static cl::opt<bool>
102ToText("to-text", cl::desc("Convert input SPIR-V binary to internal textual format"));
103
104static cl::opt<bool>
105ToBinary("to-binary",
106    cl::desc("Convert input SPIR-V in internal textual format to binary"));
107#endif
108
109static std::string
110removeExt(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
117static int
118convertLLVMToSPIRV() {
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
161static int
162convertSPIRVToLLVM() {
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
203static int
204convertSPIRV() {
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
237static int
238regularizeLLVM() {
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
288int
289main(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