Files
RedBear-OS/local/recipes/dev/spirv-llvm-translator/source/tools/llvm-spirv/llvm-spirv.cpp
T

1040 lines
37 KiB
C++

//===-- 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 SPIR-V 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 SPIR-V from stdin, write LLVM bitcode to stdout
/// llvm-spirv -r x.bil - Read SPIR-V from the x.bil file, write SPIR-V to
/// the x.bc file
///
/// Options:
/// --help - Output command line options
///
//===----------------------------------------------------------------------===//
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
#ifdef LLVM_SPIRV_HAVE_SPIRV_TOOLS
#include "spirv-tools/libspirv.hpp"
#endif
#ifndef _SPIRV_SUPPORT_TEXT_FMT
#define _SPIRV_SUPPORT_TEXT_FMT
#endif
#include "LLVMSPIRVLib.h"
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#define DEBUG_TYPE "spirv"
namespace kExt {
const char SpirvBinary[] = ".spv";
const char SpirvText[] = ".spt";
const char LLVMBinary[] = ".bc";
} // namespace kExt
using namespace llvm;
static cl::opt<std::string> InputFile(cl::Positional, cl::desc("<input file>"),
cl::init("-"));
static cl::opt<std::string> OutputFile("o",
cl::desc("Override output filename"),
cl::value_desc("filename"));
static cl::opt<bool>
IsReverse("r", cl::desc("Reverse translation (SPIR-V to LLVM)"));
static cl::opt<bool>
IsRegularization("s",
cl::desc("Regularize LLVM to be representable by SPIR-V"));
using SPIRV::VersionNumber;
static cl::opt<VersionNumber> MaxSPIRVVersion(
"spirv-max-version",
cl::desc("Choose maximum SPIR-V version which can be emitted"),
cl::values(clEnumValN(VersionNumber::SPIRV_1_0, "1.0", "SPIR-V 1.0"),
clEnumValN(VersionNumber::SPIRV_1_1, "1.1", "SPIR-V 1.1"),
clEnumValN(VersionNumber::SPIRV_1_2, "1.2", "SPIR-V 1.2"),
clEnumValN(VersionNumber::SPIRV_1_3, "1.3", "SPIR-V 1.3"),
clEnumValN(VersionNumber::SPIRV_1_4, "1.4", "SPIR-V 1.4"),
clEnumValN(VersionNumber::SPIRV_1_5, "1.5", "SPIR-V 1.5"),
clEnumValN(VersionNumber::SPIRV_1_6, "1.6", "SPIR-V 1.6")),
cl::init(VersionNumber::MaximumVersion));
static cl::list<std::string> SPIRVAllowUnknownIntrinsics(
"spirv-allow-unknown-intrinsics", cl::CommaSeparated,
cl::desc("Unknown intrinsics that begin with any prefix from the "
"comma-separated input list will be translated as external "
"function calls in SPIR-V.\nLeaving any prefix unspecified "
"(default) would naturally allow all unknown intrinsics"),
cl::value_desc("intrinsic_prefix_1,intrinsic_prefix_2"), cl::ValueOptional);
static cl::opt<bool> SPIRVGenKernelArgNameMD(
"spirv-gen-kernel-arg-name-md", cl::init(false),
cl::desc("Enable generating OpenCL kernel argument name "
"metadata"));
static cl::opt<SPIRV::ExtInst> ExtInst(
"spirv-ext-inst",
cl::desc("Specify the extended instruction set to use when "
"translating from a LLVM intrinsic function to SPIR-V. "
"If none, some LLVM intrinsic functions will be emulated."),
cl::values(clEnumValN(SPIRV::ExtInst::None, "none",
"No extended instructions"),
clEnumValN(SPIRV::ExtInst::OpenCL, "OpenCL.std",
"OpenCL.std extended instruction set")),
cl::init(SPIRV::ExtInst::None));
static cl::opt<SPIRV::BIsRepresentation> BIsRepresentation(
"spirv-target-env",
cl::desc("Specify a representation of different SPIR-V Instructions which "
"is used when translating from SPIR-V to LLVM IR"),
cl::values(
clEnumValN(SPIRV::BIsRepresentation::OpenCL12, "CL1.2", "OpenCL C 1.2"),
clEnumValN(SPIRV::BIsRepresentation::OpenCL20, "CL2.0", "OpenCL C 2.0"),
clEnumValN(SPIRV::BIsRepresentation::SPIRVFriendlyIR, "SPV-IR",
"SPIR-V Friendly IR")),
cl::init(SPIRV::BIsRepresentation::OpenCL12));
static cl::opt<bool> PreserveOCLKernelArgTypeMetadataThroughString(
"preserve-ocl-kernel-arg-type-metadata-through-string", cl::init(false),
cl::desc("Preserve OpenCL kernel_arg_type and kernel_arg_type_qual "
"metadata through OpString"));
static cl::opt<bool>
SPIRVToolsDis("spirv-tools-dis", cl::init(false),
cl::desc("Emit textual assembly using SPIRV-Tools"));
static cl::opt<bool> SPIRVEmitFunctionPtrAddrSpace(
"spirv-emit-function-ptr-addr-space", cl::init(false),
cl::desc("Emit and consume CodeSectionINTEL for function pointers"));
using SPIRV::ExtensionID;
#ifdef _SPIRV_SUPPORT_TEXT_FMT
namespace SPIRV {
// Use textual format for SPIRV.
extern bool SPIRVUseTextFormat;
} // namespace SPIRV
static cl::opt<bool>
ToText("to-text",
cl::desc("Convert input SPIR-V binary to internal textual format"));
static cl::opt<bool> ToBinary(
"to-binary",
cl::desc("Convert input SPIR-V in internal textual format to binary"));
#endif
static cl::opt<std::string> SpecConst(
"spec-const",
cl::desc("Translate SPIR-V to LLVM with constant specialization\n"
"All ids must be valid specialization constant ids for the input "
"SPIR-V module.\n"
"The list of valid ids is available via -spec-const-info option.\n"
"For duplicate ids the later one takes precedence.\n"
"Float values may be represented in decimal or hexadecimal, hex "
"values must be preceded by 0x.\n"
"Supported types are: i1, i8, i16, i32, i64, f16, f32, f64.\n"),
cl::value_desc("id1:type1:value1 id2:type2:value2 ..."));
static cl::opt<bool>
SPIRVMemToReg("spirv-mem2reg", cl::init(false),
cl::desc("LLVM/SPIR-V translation enable mem2reg"));
static cl::opt<bool>
SPIRVPreserveAuxData("spirv-preserve-auxdata", cl::init(false),
cl::desc("Preserve all auxiliary data, such as "
"function attributes and metadata"));
static cl::opt<bool> SpecConstInfo(
"spec-const-info",
cl::desc("Display id of constants available for specializaion and their "
"size in bytes"));
static cl::opt<bool>
SPIRVPrintReport("spirv-print-report", cl::init(false),
cl::desc("Display general information about the module "
"(capabilities, extensions, version, memory model"
" and addressing model)"));
static cl::opt<SPIRV::FPContractMode> FPCMode(
"spirv-fp-contract", cl::desc("Set FP Contraction mode:"),
cl::init(SPIRV::FPContractMode::On),
cl::values(
clEnumValN(SPIRV::FPContractMode::On, "on",
"choose a mode according to presence of llvm.fmuladd "
"intrinsic or `contract' flag on fp operations"),
clEnumValN(SPIRV::FPContractMode::Off, "off",
"disable FP contraction for all entry points"),
clEnumValN(
SPIRV::FPContractMode::Fast, "fast",
"allow all operations to be contracted for all entry points")));
static cl::opt<bool> SPIRVAllowExtraDIExpressions(
"spirv-allow-extra-diexpressions", cl::init(false),
cl::desc("Allow DWARF operations not listed in the OpenCL.DebugInfo.100 "
"specification (experimental, may produce incompatible SPIR-V "
"module)"));
static cl::opt<SPIRV::DebugInfoEIS> DebugEIS(
"spirv-debug-info-version", cl::desc("Set SPIR-V debug info version:"),
cl::init(SPIRV::DebugInfoEIS::OpenCL_DebugInfo_100),
cl::values(
clEnumValN(SPIRV::DebugInfoEIS::SPIRV_Debug, "legacy",
"Emit debug info compliant with the SPIRV.debug extended "
"instruction set. This option is used for compatibility "
"with older versions of the translator"),
clEnumValN(SPIRV::DebugInfoEIS::OpenCL_DebugInfo_100, "ocl-100",
"Emit debug info compliant with the OpenCL.DebugInfo.100 "
"extended instruction set. This version of SPIR-V debug "
"info format is compatible with the SPIRV-Tools"),
clEnumValN(
SPIRV::DebugInfoEIS::NonSemantic_Shader_DebugInfo_100,
"nonsemantic-shader-100",
"Emit debug info compliant with the "
"NonSemantic.Shader.DebugInfo.100 extended instruction set. This "
"version of SPIR-V debug info format is compatible with the rules "
"regarding non-semantic instruction sets."),
clEnumValN(
SPIRV::DebugInfoEIS::NonSemantic_Shader_DebugInfo_200,
"nonsemantic-shader-200",
"Emit debug info compliant with the "
"NonSemantic.Shader.DebugInfo.200 extended instruction set. This "
"version of SPIR-V debug info format is compatible with the rules "
"regarding non-semantic instruction sets.")));
static cl::opt<bool> SPIRVReplaceLLVMFmulAddWithOpenCLMad(
"spirv-replace-fmuladd-with-ocl-mad",
cl::desc("Allow replacement of llvm.fmuladd.* intrinsic with OpenCL mad "
"instruction from OpenCL extended instruction set (deprecated)"),
cl::init(true));
static cl::opt<SPIRV::BuiltinFormat> SPIRVBuiltinFormat(
"spirv-builtin-format",
cl::desc("Set LLVM-IR representation of SPIR-V builtin variables:"),
cl::init(SPIRV::BuiltinFormat::Function),
cl::values(
clEnumValN(SPIRV::BuiltinFormat::Function, "function",
"Use functions to represent SPIR-V builtin variables"),
clEnumValN(SPIRV::BuiltinFormat::Global, "global",
"Use globals to represent SPIR-V builtin variables")));
static cl::opt<bool> SPIRVUseLLVMSPIRVBackendTarget(
"spirv-use-llvm-backend-target",
cl::desc("Convert LLVM to SPIR-V using the LLVM SPIR-V Backend target if "
"it's available. Otherwise has no effect. Default behavior is to "
"don't use the LLVM SPIR-V Backend target."),
cl::init(false));
static cl::opt<uint32_t> FnVarCategory(
"fnvar-category",
cl::desc("Specify architecture category of the target device (omitting "
"this flag denotes that the target device can be of any "
"category). Used only with -r and --fnvar-spec-enable."),
cl::value_desc("category"), cl::ValueRequired);
static cl::opt<uint32_t> FnVarFamily(
"fnvar-family",
cl::desc("Specify architecture family of the target device (omitting this "
"flag denotes that the target device can be of any family). Used "
"only with -r and --fnvar-spec-enable."),
cl::value_desc("family"), cl::ValueRequired);
static cl::opt<uint32_t> FnVarArch(
"fnvar-arch",
cl::desc("Specify architecture of the target device (omitting this flag "
"denotes that the target device can be of any architecture). Used "
"only with -r and --fnvar-spec-enable."),
cl::value_desc("architecture"), cl::ValueRequired);
static cl::opt<uint32_t>
FnVarTarget("fnvar-target",
cl::desc("Specify target of the target device (omitting this "
"flag denotes that the target device can be any "
"target). Used only with -r and --fnvar-spec-enable."),
cl::value_desc("target"), cl::ValueRequired);
static cl::list<uint32_t> FnVarFeatures(
"fnvar-features", cl::CommaSeparated,
cl::desc("Specify features of the target device (omitting this flag "
"denotes that the target device supports all features). Used only "
"with -r and --fnvar-spec-enable."),
cl::value_desc("feature0,feature1,..."), cl::ValueRequired);
static cl::list<uint32_t> FnVarCapabilities(
"fnvar-capabilities", cl::CommaSeparated,
cl::desc("Specify capabilities of the target device (omitting this flag "
"denotes that the target device supports all features). Used only "
"with -r and --fnvar-spec-enable."),
cl::value_desc("capability0,capability1,..."), cl::ValueRequired);
static cl::opt<std::string> FnVarSpvOut(
"fnvar-spv-out",
cl::desc("Save the specialized target-specific SPIR-V module to this file. "
"Used only with -r and --fnvar-spec-enable."),
cl::value_desc("file"), cl::ValueRequired);
static cl::opt<bool> FnVarSpecEnable(
"fnvar-spec-enable", cl::init(false),
cl::desc("Enable specialization of function variants according to "
"SPV_INTEL_function_variants. Requires -r flag."));
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 ExitOnError ExitOnErr;
#ifdef LLVM_SPIRV_HAVE_SPIRV_TOOLS
/// Stream buffer that captures written data into a vector and allows reading
/// the data back as an array of uint32_t's.
class StreambufToArray : public std::streambuf {
public:
const uint32_t *data() const {
return reinterpret_cast<const uint32_t *>(Buffer.data());
}
size_t size() const { return Buffer.size() / sizeof(uint32_t); }
protected:
std::streamsize xsputn(const char *s, std::streamsize count) override {
for (std::streamsize I = 0; I < count; I++) {
Buffer.push_back(s[I]);
}
return count;
}
private:
std::vector<char> Buffer;
};
#endif // LLVM_SPIRV_HAVE_SPIRV_TOOLS
static int convertLLVMToSPIRV(const SPIRV::TranslatorOpts &Opts) {
LLVMContext Context;
std::unique_ptr<MemoryBuffer> MB =
ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFile)));
std::unique_ptr<Module> M =
ExitOnErr(getOwningLazyBitcodeModule(std::move(MB), Context,
/*ShouldLazyLoadMetadata=*/true));
ExitOnErr(M->materializeAll());
if (OutputFile.empty()) {
if (InputFile == "-")
OutputFile = "-";
else
OutputFile =
removeExt(InputFile) +
(SPIRV::SPIRVUseTextFormat ? kExt::SpirvText : kExt::SpirvBinary);
}
if (SPIRVToolsDis) {
#ifdef LLVM_SPIRV_HAVE_SPIRV_TOOLS
auto DisMessagePrinter = [](spv_message_level_t Level, const char *source,
const spv_position_t &position,
const char *message) -> void {
errs() << source << ": " << message << "\n";
};
spvtools::SpirvTools SpvTool(SPV_ENV_OPENCL_2_0);
SpvTool.SetMessageConsumer(DisMessagePrinter);
std::string DisOutput;
// Serialize the SPIR-V module to a uint32_t array and then invoke the
// SPIR-V tools disassembler to obtain textual assembly.
std::string Err;
StreambufToArray OutStreamBuf;
std::ostream OutStream(&OutStreamBuf);
bool Success = writeSpirv(M.get(), Opts, OutStream, Err);
if (!Success) {
errs() << "Failed to translate SPIR-V: " << Err << '\n';
return -1;
}
if (!SpvTool.Disassemble(OutStreamBuf.data(), OutStreamBuf.size(),
&DisOutput)) {
errs() << "Failed to generate textual assembly\n";
return -1;
}
if (OutputFile != "-") {
std::ofstream OutFile(OutputFile, std::ios::binary);
OutFile << DisOutput;
} else {
std::cout << DisOutput;
}
return 0;
#else
errs() << "llvm-spirv was built without --spirv-tools-dis support\n";
return -1;
#endif // LLVM_SPIRV_HAVE_SPIRV_TOOLS
}
std::string Err;
bool Success = false;
if (OutputFile != "-") {
std::ofstream OutFile(OutputFile, std::ios::binary);
Success = writeSpirv(M.get(), Opts, OutFile, Err);
} else {
Success = writeSpirv(M.get(), Opts, std::cout, Err);
}
if (!Success) {
errs() << "Fails to save LLVM as SPIR-V: " << Err << '\n';
return -1;
}
return 0;
}
static bool isFileEmpty(const std::string &FileName) {
std::ifstream File(FileName);
return File && File.peek() == EOF;
}
static int convertSPIRVToLLVM(const SPIRV::TranslatorOpts &Opts) {
LLVMContext Context;
std::ifstream IFS(InputFile, std::ios::binary);
Module *M;
std::string Err;
if (!readSpirv(Context, Opts, IFS, M, Err)) {
errs() << "Fails to load SPIR-V as LLVM Module: " << Err << '\n';
return -1;
}
LLVM_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;
ToolOutputFile Out(OutputFile.c_str(), EC, sys::fs::OF_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 = [&](std::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 == "-")
return Action(std::cout);
// Open the output file in binary mode in case we convert text to SPIRV binary
if (ToBinary) {
std::ofstream OFS(OutputFile, std::ios::binary);
return Action(OFS);
}
// Convert SPIRV binary to text
std::ofstream OFS(OutputFile);
return Action(OFS);
}
#endif
static int regularizeLLVM(SPIRV::TranslatorOpts &Opts) {
LLVMContext Context;
std::unique_ptr<MemoryBuffer> MB =
ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFile)));
std::unique_ptr<Module> M =
ExitOnErr(getOwningLazyBitcodeModule(std::move(MB), Context,
/*ShouldLazyLoadMetadata=*/true));
ExitOnErr(M->materializeAll());
if (OutputFile.empty()) {
if (InputFile == "-")
OutputFile = "-";
else
OutputFile = removeExt(InputFile) + ".regularized.bc";
}
std::string Err;
if (!regularizeLlvmForSpirv(M.get(), Err, Opts)) {
errs() << "Fails to save LLVM as SPIR-V: " << Err << '\n';
return -1;
}
std::error_code EC;
ToolOutputFile Out(OutputFile.c_str(), EC, sys::fs::OF_None);
if (EC) {
errs() << "Fails to open output file: " << EC.message();
return -1;
}
WriteBitcodeToFile(*M.get(), Out.os());
Out.keep();
return 0;
}
static int parseSPVExtOption(
cl::list<std::string> &SPVExtList,
SPIRV::TranslatorOpts::ExtensionsStatusMap &ExtensionsStatus) {
// Map name -> id for known extensions
std::map<std::string, ExtensionID> ExtensionNamesMap;
#define _STRINGIFY(X) #X
#define STRINGIFY(X) _STRINGIFY(X)
#define EXT(X) ExtensionNamesMap[STRINGIFY(X)] = ExtensionID::X;
#include "LLVMSPIRVExtensions.inc"
#undef EXT
#undef STRINGIFY
#undef _STRINGIFY
// Set the initial state:
// - during SPIR-V consumption, assume that any known extension is allowed.
// - during SPIR-V generation, assume that any known extension is disallowed.
// - during conversion to/from SPIR-V text representation, assume that any
// known extension is allowed.
std::optional<bool> DefaultVal;
if (IsReverse)
DefaultVal = true;
for (const auto &It : ExtensionNamesMap)
ExtensionsStatus[It.second] = DefaultVal;
if (SPVExtList.empty())
return 0; // Nothing to do
for (unsigned i = 0; i < SPVExtList.size(); ++i) {
const std::string &ExtString = SPVExtList[i];
if (ExtString.empty() ||
('+' != ExtString.front() && '-' != ExtString.front())) {
errs() << "Invalid value of --spirv-ext, expected format is:\n"
<< "\t--spirv-ext=+EXT_NAME,-EXT_NAME\n";
return -1;
}
auto ExtName = ExtString.substr(1);
if (ExtName.empty()) {
errs() << "Invalid value of --spirv-ext, expected format is:\n"
<< "\t--spirv-ext=+EXT_NAME,-EXT_NAME\n";
return -1;
}
bool ExtStatus = ('+' == ExtString.front());
if ("all" == ExtName) {
// Update status for all known extensions
for (const auto &It : ExtensionNamesMap)
ExtensionsStatus[It.second] = ExtStatus;
} else {
// Reject unknown extensions
const auto &It = ExtensionNamesMap.find(ExtName);
if (ExtensionNamesMap.end() == It) {
errs() << "Unknown extension '" << ExtName << "' was specified via "
<< "--spirv-ext option\n";
return -1;
}
ExtensionsStatus[It->second] = ExtStatus;
}
}
return 0;
}
// Returns true on error.
bool parseSpecConstOpt(llvm::StringRef SpecConstStr,
SPIRV::TranslatorOpts &Opts) {
std::ifstream IFS(InputFile, std::ios::binary);
std::vector<SpecConstInfoTy> SpecConstInfo;
if (!getSpecConstInfo(IFS, SpecConstInfo))
return true;
SmallVector<StringRef, 8> Split;
SpecConstStr.split(Split, ' ', -1, false);
for (StringRef Option : Split) {
SmallVector<StringRef, 4> Params;
Option.split(Params, ':', 2);
if (Params.size() < 3) {
errs() << "Error: Invalid format of -" << SpecConst.ArgStr
<< " option: \"" << Option << "\". Expected format: -"
<< SpecConst.ArgStr << " \"<" << SpecConst.ValueStr << ">\"\n";
return true;
}
uint32_t SpecId;
if (Params[0].getAsInteger(10, SpecId)) {
errs() << "Error: Invalid id for '-" << SpecConst.ArgStr
<< "' option! In \"" << Option << "\": \"" << Params[0]
<< "\" must be a 32-bit unsigned integer\n";
return true;
}
auto It =
std::find_if(SpecConstInfo.begin(), SpecConstInfo.end(),
[=](SpecConstInfoTy Info) { return Info.ID == SpecId; });
if (It == SpecConstInfo.end()) {
errs() << "Error: CL_INVALID_SPEC_ID. \"" << Option << "\": There is no "
<< "specialization constant with id = " << SpecId
<< " in the SPIR-V module.";
return true;
}
if (Params[1].consume_front("i")) {
unsigned Width = 0;
Params[1].getAsInteger(10, Width);
if (!isPowerOf2_32(Width) || Width > 64) {
errs() << "Error: Invalid type for '-" << SpecConst.ArgStr
<< "' option! In \"" << Option << "\": \"i" << Params[1]
<< "\" - is not allowed type. "
<< "Allowed types are: i1, i8, i16, i32, i64, f16, f32, f64\n";
return true;
}
size_t Size = Width < 8 ? 1 : Width / 8;
if (Size != It->Size) {
errs() << "Error: CL_INVALID_VALUE. In \"" << Option << "\": Size of "
<< "type i" << Width << " (" << Size << " bytes) "
<< "does not match the size of the specialization constant "
<< "in the module (" << It->Size << " bytes)\n";
return true;
}
APInt Value;
bool Err = Params[2].getAsInteger(10, Value);
if (Err || Value.getActiveWords() > 1 ||
(Width < 64 && Value.getZExtValue() >> Width)) {
errs() << "Error: Invalid value for '-" << SpecConst.ArgStr
<< "' option! In \"" << Option << "\": can't convert \""
<< Params[2] << "\" to " << Width << "-bit integer number\n";
return true;
}
Opts.setSpecConst(SpecId, Value.getZExtValue());
} else if (Params[1].consume_front("f")) {
unsigned Width = 0;
Params[1].getAsInteger(10, Width);
const llvm::fltSemantics *FS = nullptr;
switch (Width) {
case 16:
FS = &APFloat::IEEEhalf();
break;
case 32:
FS = &APFloat::IEEEsingle();
break;
case 64:
FS = &APFloat::IEEEdouble();
break;
default:
errs() << "Error: Invalid type for '-" << SpecConst.ArgStr
<< "' option! In \"" << Option << "\": \"f" << Params[1]
<< "\" - is not allowed type. "
<< "Allowed types are: i1, i8, i16, i32, i64, f16, f32, f64\n";
return true;
}
APFloat Value(*FS);
if (Params[2].find("0x") != StringRef::npos) {
std::stringstream paramStream;
paramStream << std::hex << Params[2].data();
uint64_t specVal = 0;
paramStream >> specVal;
Opts.setSpecConst(SpecId, specVal);
} else {
Expected<APFloat::opStatus> StatusOrErr =
Value.convertFromString(Params[2], APFloat::rmNearestTiesToEven);
if (!StatusOrErr) {
return true;
}
// It's ok to have inexact conversion from decimal representation.
APFloat::opStatus Status = *StatusOrErr;
if (Status & ~APFloat::opInexact) {
errs() << "Error: Invalid value for '-" << SpecConst.ArgStr
<< "' option! In \"" << Option << "\": can't convert \""
<< Params[2] << "\" to " << Width
<< "-bit floating point number\n";
return true;
}
Opts.setSpecConst(SpecId, Value.bitcastToAPInt().getZExtValue());
}
} else {
errs() << "Error: Invalid type for '-" << SpecConst.ArgStr
<< "' option! In \"" << Option << "\": \"" << Params[1]
<< "\" - is not allowed type. "
<< "Allowed types are: i1, i8, i16, i32, i64, f16, f32, f64\n";
return true;
}
}
return false;
}
static void parseAllowUnknownIntrinsicsOpt(SPIRV::TranslatorOpts &Opts) {
SPIRV::TranslatorOpts::ArgList PrefixList;
for (const auto &Prefix : SPIRVAllowUnknownIntrinsics) {
PrefixList.push_back(Prefix);
}
Opts.setSPIRVAllowUnknownIntrinsics(PrefixList);
}
int main(int Ac, char **Av) {
EnablePrettyStackTrace();
sys::PrintStackTraceOnErrorSignal(Av[0]);
PrettyStackTraceProgram X(Ac, Av);
#if defined(LLVM_SPIRV_BACKEND_TARGET_PRESENT)
// SPIR-V Backend is available, and so we have a clash of command line
// argument names, because both products use "spirv-ext" name. Let's rename
// the command line option coming from SPIR-V Backend, as it's not supposed to
// be used by a user anyway. After that we may safely add the instance of
// "spirv-ext" required by LLVM/SPIRV Translator from the corresponding auto
// variable (SPVExt).
StringMap<llvm::cl::Option *> &RegisteredOptions =
llvm::cl::getRegisteredOptions();
if (RegisteredOptions.count("spirv-ext") == 1) {
llvm::cl::Option *OptToDisable = RegisteredOptions["spirv-ext"];
OptToDisable->setArgStr("spirv-ext-coming-from-spirv-backend");
OptToDisable->setHiddenFlag(cl::Hidden);
}
#endif
cl::list<std::string> SPVExt(
"spirv-ext", cl::CommaSeparated,
cl::desc("Specify list of allowed/disallowed extensions"),
cl::value_desc("+SPV_extenstion1_name,-SPV_extension2_name"),
cl::ValueRequired);
cl::ParseCommandLineOptions(Ac, Av, "LLVM/SPIR-V translator");
if (InputFile != "-" && isFileEmpty(InputFile)) {
errs() << "Can't translate, file is empty\n";
return -1;
}
SPIRV::TranslatorOpts::ExtensionsStatusMap ExtensionsStatus;
// ExtensionsStatus will be properly initialized and update according to
// values passed via --spirv-ext option in parseSPVExtOption function.
int Ret = parseSPVExtOption(SPVExt, ExtensionsStatus);
if (0 != Ret)
return Ret;
SPIRV::TranslatorOpts Opts(MaxSPIRVVersion, ExtensionsStatus);
#if defined(LLVM_SPIRV_BACKEND_TARGET_PRESENT)
Opts.setUseLLVMTarget(SPIRVUseLLVMSPIRVBackendTarget);
#endif
if (ExtInst.getNumOccurrences() != 0) {
if (ExtInst.getNumOccurrences() > 1) {
errs() << "Error: --spirv-ext-inst cannot be used more than once\n";
return -1;
} else if (SPIRVReplaceLLVMFmulAddWithOpenCLMad.getNumOccurrences()) {
errs()
<< "Error: --spirv-ext-inst and --spirv-replace-fmuladd-with-ocl-mad "
"cannot be used together. --spirv-replace-fmuladd-with-ocl-mad "
"is deprecated and --spirv-ext-inst is preferred.\n";
return -1;
} else if (IsReverse) {
errs() << "Note: --spirv-ext-inst option ignored as it only "
"affects translation from LLVM IR to SPIR-V";
}
Opts.setExtInst(ExtInst);
}
if (BIsRepresentation.getNumOccurrences() != 0) {
if (!IsReverse) {
errs() << "Note: --spirv-target-env option ignored as it only "
"affects translation from SPIR-V to LLVM IR";
} else {
Opts.setDesiredBIsRepresentation(BIsRepresentation);
}
}
Opts.setFPContractMode(FPCMode);
if (SPIRVBuiltinFormat.getNumOccurrences() != 0) {
if (!IsReverse) {
errs() << "Note: --spirv-builtin-format option ignored as it only "
"affects translation from SPIR-V to LLVM IR";
} else {
Opts.setBuiltinFormat(SPIRVBuiltinFormat);
}
}
if (SPIRVMemToReg)
Opts.setMemToRegEnabled(SPIRVMemToReg);
if (SPIRVGenKernelArgNameMD)
Opts.setGenKernelArgNameMDEnabled(SPIRVGenKernelArgNameMD);
if (IsReverse && !SpecConst.empty()) {
if (parseSpecConstOpt(SpecConst, Opts))
return -1;
}
if (SPIRVPreserveAuxData) {
Opts.setPreserveAuxData(SPIRVPreserveAuxData);
if (!IsReverse)
Opts.setAllowedToUseExtension(
SPIRV::ExtensionID::SPV_KHR_non_semantic_info);
}
if (SPIRVAllowUnknownIntrinsics.getNumOccurrences() != 0) {
if (IsReverse) {
errs()
<< "Note: --spirv-allow-unknown-intrinsics option ignored as it only "
"affects translation from LLVM IR to SPIR-V";
} else {
parseAllowUnknownIntrinsicsOpt(Opts);
}
}
if (SPIRVReplaceLLVMFmulAddWithOpenCLMad.getNumOccurrences() != 0) {
if (IsReverse) {
errs() << "Note: --spirv-replace-fmuladd-with-ocl-mad option ignored as "
"it only affects translation from LLVM IR to SPIR-V";
} else {
Opts.setReplaceLLVMFmulAddWithOpenCLMad(
SPIRVReplaceLLVMFmulAddWithOpenCLMad);
}
}
if (SPIRVAllowExtraDIExpressions.getNumOccurrences() != 0) {
Opts.setAllowExtraDIExpressionsEnabled(SPIRVAllowExtraDIExpressions);
}
if (DebugEIS.getNumOccurrences() != 0) {
if (IsReverse) {
errs() << "Note: --spirv-debug-info-version option ignored as it only "
"affects translation from LLVM IR to SPIR-V";
} else {
Opts.setDebugInfoEIS(DebugEIS);
if (DebugEIS.getValue() ==
SPIRV::DebugInfoEIS::NonSemantic_Shader_DebugInfo_200)
Opts.setAllowExtraDIExpressionsEnabled(true);
if (DebugEIS.getValue() ==
SPIRV::DebugInfoEIS::NonSemantic_Shader_DebugInfo_100 ||
DebugEIS.getValue() ==
SPIRV::DebugInfoEIS::NonSemantic_Shader_DebugInfo_200)
Opts.setAllowedToUseExtension(
SPIRV::ExtensionID::SPV_KHR_non_semantic_info);
}
}
if (PreserveOCLKernelArgTypeMetadataThroughString.getNumOccurrences() != 0)
Opts.setPreserveOCLKernelArgTypeMetadataThroughString(true);
if (SPIRVEmitFunctionPtrAddrSpace.getNumOccurrences() != 0)
Opts.setEmitFunctionPtrAddrSpace(true);
Opts.setFnVarSpecEnable(FnVarSpecEnable);
if (!IsReverse &&
(FnVarSpecEnable || FnVarCategory != 0 || FnVarFamily != 0 ||
FnVarArch != 0 || FnVarTarget != 0 || !FnVarFeatures.empty() ||
!FnVarCapabilities.empty() || !FnVarSpvOut.empty())) {
errs() << "--fnvar-xxx flags can be used only with -r\n";
return -1;
}
if (!FnVarSpecEnable &&
(FnVarCategory != 0 || FnVarFamily != 0 || FnVarArch != 0 ||
FnVarTarget != 0 || !FnVarFeatures.empty() ||
!FnVarCapabilities.empty() || !FnVarSpvOut.empty())) {
errs() << "--fnvar-xxx flags need to be enabled with --fnvar-spec-enable\n";
return -1;
}
if (FnVarCategory.getNumOccurrences() > 0) {
Opts.setFnVarCategory(FnVarCategory);
}
if (FnVarFamily.getNumOccurrences() > 0) {
Opts.setFnVarFamily(FnVarFamily);
}
if (FnVarArch.getNumOccurrences() > 0) {
Opts.setFnVarArch(FnVarArch);
}
if (FnVarTarget.getNumOccurrences() > 0) {
Opts.setFnVarTarget(FnVarTarget);
}
if (!FnVarFeatures.empty()) {
Opts.setFnVarFeatures(FnVarFeatures);
}
if (!FnVarCapabilities.empty()) {
Opts.setFnVarCapabilities(FnVarCapabilities);
}
if (!FnVarSpvOut.empty()) {
Opts.setFnVarSpvOut(FnVarSpvOut);
}
if (!Opts.validateFnVarOpts()) {
return -1;
}
#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 && !SpecConstInfo && !SPIRVPrintReport)
return convertLLVMToSPIRV(Opts);
if (IsReverse && IsRegularization) {
errs() << "Cannot have both -r and -s options\n";
return -1;
}
if (IsReverse)
return convertSPIRVToLLVM(Opts);
if (IsRegularization)
return regularizeLLVM(Opts);
if (SpecConstInfo) {
std::ifstream IFS(InputFile, std::ios::binary);
std::vector<SpecConstInfoTy> SpecConstInfo;
if (!getSpecConstInfo(IFS, SpecConstInfo)) {
std::cout << "Invalid SPIR-V binary";
return -1;
}
std::cout << "Number of scalar specialization constants in the module = "
<< SpecConstInfo.size() << "\n";
for (auto &SpecConst : SpecConstInfo)
std::cout << "Spec const id = " << SpecConst.ID
<< ", size in bytes = " << SpecConst.Size
<< ", type = " << SpecConst.Type << "\n";
}
if (SPIRVPrintReport) {
std::ifstream IFS(InputFile, std::ios::binary);
int ErrCode = 0;
std::optional<SPIRV::SPIRVModuleReport> BinReport =
SPIRV::getSpirvReport(IFS, ErrCode);
if (!BinReport) {
std::cerr << "Invalid SPIR-V binary: \""
<< SPIRV::getErrorMessage(ErrCode) << "\"\n";
return -1;
}
SPIRV::SPIRVModuleTextReport TextReport =
SPIRV::formatSpirvReport(BinReport.value());
std::cout << "SPIR-V module report:" << "\n Version: " << TextReport.Version
<< "\n Memory model: " << TextReport.MemoryModel
<< "\n Addressing model: " << TextReport.AddrModel << "\n";
std::cout << " Number of capabilities: " << TextReport.Capabilities.size()
<< "\n";
for (auto &Capability : TextReport.Capabilities)
std::cout << " Capability: " << Capability << "\n";
std::cout << " Number of extensions: " << TextReport.Extensions.size()
<< "\n";
for (auto &Extension : TextReport.Extensions)
std::cout << " Extension: " << Extension << "\n";
std::cout << " Number of extended instruction sets: "
<< TextReport.ExtendedInstructionSets.size() << "\n";
for (auto &ExtendedInstructionSet : TextReport.ExtendedInstructionSets)
std::cout << " Extended Instruction Set: " << ExtendedInstructionSet
<< "\n";
}
return 0;
}