Skip to content

Commit a130112

Browse files
VyacheslavLevytskyysys-ce-bb
authored andcommitted
add API call to display general information about the module (#2298)
Partially load SPIR-V from the stream and decode only selected for the report instructions, needed to retrieve general information about the module: capabilities, extensions, version, memory model and addressing model. In addition to immediately helpful for back-ends lists of capabilities and extensions declared in SPIR-V module, a general intent also is to extend report details in future by feedbacks about further potentially useful analysis, statistics, etc. Original commit: KhronosGroup/SPIRV-LLVM-Translator@918036c
1 parent ba6699d commit a130112

File tree

8 files changed

+306
-25
lines changed

8 files changed

+306
-25
lines changed

llvm-spirv/include/LLVMSPIRVLib.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,35 @@ std::unique_ptr<SPIRVModule> readSpirvModule(std::istream &IS,
106106
const SPIRV::TranslatorOpts &Opts,
107107
std::string &ErrMsg);
108108

109+
struct SPIRVModuleReport {
110+
SPIRV::VersionNumber Version;
111+
uint32_t MemoryModel;
112+
uint32_t AddrModel;
113+
std::vector<std::string> Extensions;
114+
std::vector<std::string> ExtendedInstructionSets;
115+
std::vector<uint32_t> Capabilities;
116+
};
117+
/// \brief Partially load SPIR-V from the stream and decode only selected
118+
/// instructions that are needed to retrieve general information
119+
/// about the module. If this call fails, readSPIRVModule is
120+
/// expected to fail as well.
121+
/// \returns nullopt on failure.
122+
std::optional<SPIRVModuleReport> getSpirvReport(std::istream &IS);
123+
std::optional<SPIRVModuleReport> getSpirvReport(std::istream &IS, int &ErrCode);
124+
125+
struct SPIRVModuleTextReport {
126+
std::string Version;
127+
std::string MemoryModel;
128+
std::string AddrModel;
129+
std::vector<std::string> Extensions;
130+
std::vector<std::string> ExtendedInstructionSets;
131+
std::vector<std::string> Capabilities;
132+
};
133+
/// \brief Create a human-readable form of the report returned by a call to
134+
/// getSpirvReport by decoding its binary fields.
135+
/// \returns String with the human-readable report.
136+
SPIRVModuleTextReport formatSpirvReport(const SPIRVModuleReport &Report);
137+
109138
} // End namespace SPIRV
110139

111140
namespace llvm {

llvm-spirv/include/LLVMSPIRVOpts.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,27 @@ enum class VersionNumber : uint32_t {
6868
MaximumVersion = SPIRV_1_4
6969
};
7070

71+
inline constexpr std::string_view formatVersionNumber(uint32_t Version) {
72+
switch (Version) {
73+
case static_cast<uint32_t>(VersionNumber::SPIRV_1_0):
74+
return "1.0";
75+
case static_cast<uint32_t>(VersionNumber::SPIRV_1_1):
76+
return "1.1";
77+
case static_cast<uint32_t>(VersionNumber::SPIRV_1_2):
78+
return "1.2";
79+
case static_cast<uint32_t>(VersionNumber::SPIRV_1_3):
80+
return "1.3";
81+
case static_cast<uint32_t>(VersionNumber::SPIRV_1_4):
82+
return "1.4";
83+
}
84+
return "unknown";
85+
}
86+
87+
inline bool isSPIRVVersionKnown(uint32_t Ver) {
88+
return Ver >= static_cast<uint32_t>(VersionNumber::MinimumVersion) &&
89+
Ver <= static_cast<uint32_t>(VersionNumber::MaximumVersion);
90+
}
91+
7192
enum class ExtensionID : uint32_t {
7293
First,
7394
#define EXT(X) X,

llvm-spirv/lib/SPIRV/SPIRVReader.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4848,6 +4848,137 @@ Instruction *SPIRVToLLVM::transRelational(SPIRVInstruction *I, BasicBlock *BB) {
48484848
return cast<Instruction>(Mutator.getMutated());
48494849
}
48504850

4851+
std::optional<SPIRVModuleReport> getSpirvReport(std::istream &IS) {
4852+
int IgnoreErrCode;
4853+
return getSpirvReport(IS, IgnoreErrCode);
4854+
}
4855+
4856+
std::optional<SPIRVModuleReport> getSpirvReport(std::istream &IS,
4857+
int &ErrCode) {
4858+
SPIRVWord Word;
4859+
std::string Name;
4860+
std::unique_ptr<SPIRVModule> BM(SPIRVModule::createSPIRVModule());
4861+
SPIRVDecoder D(IS, *BM);
4862+
D >> Word;
4863+
if (Word != MagicNumber) {
4864+
ErrCode = SPIRVEC_InvalidMagicNumber;
4865+
return {};
4866+
}
4867+
D >> Word;
4868+
if (!isSPIRVVersionKnown(Word)) {
4869+
ErrCode = SPIRVEC_InvalidVersionNumber;
4870+
return {};
4871+
}
4872+
SPIRVModuleReport Report;
4873+
Report.Version = static_cast<SPIRV::VersionNumber>(Word);
4874+
// Skip: Generator’s magic number, Bound and Reserved word
4875+
D.ignore(3);
4876+
4877+
bool IsReportGenCompleted = false, IsMemoryModelDefined = false;
4878+
while (!IS.bad() && !IsReportGenCompleted && D.getWordCountAndOpCode()) {
4879+
switch (D.OpCode) {
4880+
case OpCapability:
4881+
D >> Word;
4882+
Report.Capabilities.push_back(Word);
4883+
break;
4884+
case OpExtension:
4885+
Name.clear();
4886+
D >> Name;
4887+
Report.Extensions.push_back(Name);
4888+
break;
4889+
case OpExtInstImport:
4890+
Name.clear();
4891+
D >> Word >> Name;
4892+
Report.ExtendedInstructionSets.push_back(Name);
4893+
break;
4894+
case OpMemoryModel:
4895+
if (IsMemoryModelDefined) {
4896+
ErrCode = SPIRVEC_RepeatedMemoryModel;
4897+
return {};
4898+
}
4899+
SPIRVAddressingModelKind AddrModel;
4900+
SPIRVMemoryModelKind MemoryModel;
4901+
D >> AddrModel >> MemoryModel;
4902+
if (!isValid(AddrModel)) {
4903+
ErrCode = SPIRVEC_InvalidAddressingModel;
4904+
return {};
4905+
}
4906+
if (!isValid(MemoryModel)) {
4907+
ErrCode = SPIRVEC_InvalidMemoryModel;
4908+
return {};
4909+
}
4910+
Report.MemoryModel = MemoryModel;
4911+
Report.AddrModel = AddrModel;
4912+
IsMemoryModelDefined = true;
4913+
// In this report we don't analyze instructions after OpMemoryModel
4914+
IsReportGenCompleted = true;
4915+
break;
4916+
default:
4917+
// No more instructions to gather information about
4918+
IsReportGenCompleted = true;
4919+
}
4920+
}
4921+
if (IS.bad()) {
4922+
ErrCode = SPIRVEC_InvalidModule;
4923+
return {};
4924+
}
4925+
if (!IsMemoryModelDefined) {
4926+
ErrCode = SPIRVEC_UnspecifiedMemoryModel;
4927+
return {};
4928+
}
4929+
ErrCode = SPIRVEC_Success;
4930+
return std::make_optional(std::move(Report));
4931+
}
4932+
4933+
constexpr std::string_view formatAddressingModel(uint32_t AddrModel) {
4934+
switch (AddrModel) {
4935+
case AddressingModelLogical:
4936+
return "Logical";
4937+
case AddressingModelPhysical32:
4938+
return "Physical32";
4939+
case AddressingModelPhysical64:
4940+
return "Physical64";
4941+
case AddressingModelPhysicalStorageBuffer64:
4942+
return "PhysicalStorageBuffer64";
4943+
default:
4944+
return "Unknown";
4945+
}
4946+
}
4947+
4948+
constexpr std::string_view formatMemoryModel(uint32_t MemoryModel) {
4949+
switch (MemoryModel) {
4950+
case MemoryModelSimple:
4951+
return "Simple";
4952+
case MemoryModelGLSL450:
4953+
return "GLSL450";
4954+
case MemoryModelOpenCL:
4955+
return "OpenCL";
4956+
case MemoryModelVulkan:
4957+
return "Vulkan";
4958+
default:
4959+
return "Unknown";
4960+
}
4961+
}
4962+
4963+
SPIRVModuleTextReport formatSpirvReport(const SPIRVModuleReport &Report) {
4964+
SPIRVModuleTextReport TextReport;
4965+
TextReport.Version =
4966+
formatVersionNumber(static_cast<uint32_t>(Report.Version));
4967+
TextReport.AddrModel = formatAddressingModel(Report.AddrModel);
4968+
TextReport.MemoryModel = formatMemoryModel(Report.MemoryModel);
4969+
// format capability codes as strings
4970+
std::string Name;
4971+
for (auto Capability : Report.Capabilities) {
4972+
bool Found = SPIRVCapabilityNameMap::find(
4973+
static_cast<SPIRVCapabilityKind>(Capability), &Name);
4974+
TextReport.Capabilities.push_back(Found ? Name : "Unknown");
4975+
}
4976+
// other fields with string content can be copied as is
4977+
TextReport.Extensions = Report.Extensions;
4978+
TextReport.ExtendedInstructionSets = Report.ExtendedInstructionSets;
4979+
return TextReport;
4980+
}
4981+
48514982
std::unique_ptr<SPIRVModule> readSpirvModule(std::istream &IS,
48524983
const SPIRV::TranslatorOpts &Opts,
48534984
std::string &ErrMsg) {

llvm-spirv/lib/SPIRV/libSPIRV/SPIRVErrorEnum.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@ _SPIRV_OP(InvalidWordCount,
2222
_SPIRV_OP(Requires1_1, "Feature requires SPIR-V 1.1 or greater:")
2323
_SPIRV_OP(RequiresExtension,
2424
"Feature requires the following SPIR-V extension:\n")
25+
_SPIRV_OP(InvalidMagicNumber,
26+
"Invalid Magic Number.")
27+
_SPIRV_OP(InvalidVersionNumber,
28+
"Invalid Version Number.")
29+
_SPIRV_OP(UnspecifiedMemoryModel, "Unspecified Memory Model.")
30+
_SPIRV_OP(RepeatedMemoryModel, "Expects a single OpMemoryModel instruction.")

llvm-spirv/lib/SPIRV/libSPIRV/SPIRVModule.cpp

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2094,27 +2094,7 @@ void SPIRVModuleImpl::addUnknownStructField(SPIRVTypeStruct *Struct, unsigned I,
20942094
}
20952095

20962096
static std::string to_string(uint32_t Version) {
2097-
std::string Res;
2098-
switch (Version) {
2099-
case static_cast<uint32_t>(VersionNumber::SPIRV_1_0):
2100-
Res = "1.0";
2101-
break;
2102-
case static_cast<uint32_t>(VersionNumber::SPIRV_1_1):
2103-
Res = "1.1";
2104-
break;
2105-
case static_cast<uint32_t>(VersionNumber::SPIRV_1_2):
2106-
Res = "1.2";
2107-
break;
2108-
case static_cast<uint32_t>(VersionNumber::SPIRV_1_3):
2109-
Res = "1.3";
2110-
break;
2111-
case static_cast<uint32_t>(VersionNumber::SPIRV_1_4):
2112-
Res = "1.4";
2113-
break;
2114-
default:
2115-
Res = "unknown";
2116-
}
2117-
2097+
std::string Res(formatVersionNumber(Version));
21182098
Res += " (" + std::to_string(Version) + ")";
21192099
return Res;
21202100
}
@@ -2139,9 +2119,7 @@ std::istream &operator>>(std::istream &I, SPIRVModule &M) {
21392119
}
21402120

21412121
Decoder >> MI.SPIRVVersion;
2142-
bool SPIRVVersionIsKnown =
2143-
static_cast<uint32_t>(VersionNumber::MinimumVersion) <= MI.SPIRVVersion &&
2144-
MI.SPIRVVersion <= static_cast<uint32_t>(VersionNumber::MaximumVersion);
2122+
bool SPIRVVersionIsKnown = isSPIRVVersionKnown(MI.SPIRVVersion);
21452123
if (!M.getErrorLog().checkError(
21462124
SPIRVVersionIsKnown, SPIRVEC_InvalidModule,
21472125
"unsupported SPIR-V version number '" + to_string(MI.SPIRVVersion) +
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
; RUN: llvm-spirv %s -to-binary -o %t.spv
2+
; The next line is to corrupt the binary file by changing its Magic Number
3+
; RUN: echo "0" > %t_corrupted.spv && cat %t.spv >> %t_corrupted.spv
4+
; RUN: not llvm-spirv --spirv-print-report %t_corrupted.spv 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
5+
;
6+
; CHECK-ERROR: Invalid SPIR-V binary
7+
8+
119734787 65536 393230 10 0
9+
2 Capability Addresses
10+
2 Capability Kernel
11+
2 Capability LoopFuseINTEL
12+
2 Capability BitInstructions
13+
6 Extension "SPV_INTEL_loop_fuse"
14+
8 Extension "SPV_KHR_bit_instructions"
15+
5 ExtInstImport 1 "OpenCL.std"
16+
3 MemoryModel 1 2
17+
7 EntryPoint 6 5 "TestSatPacked"
18+
3 Source 3 102000
19+
20+
5 Decorate 5 FuseLoopsInFunctionINTEL 3 1
21+
4 TypeInt 3 32 0
22+
2 TypeVoid 2
23+
5 TypeFunction 4 2 3 3
24+
25+
5 Function 2 5 0 4
26+
3 FunctionParameter 3 6
27+
3 FunctionParameter 3 7
28+
29+
2 Label 8
30+
4 BitReverse 3 9 6
31+
1 Return
32+
33+
1 FunctionEnd

llvm-spirv/test/spirv_report.spt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
; RUN: llvm-spirv %s -to-binary -o %t.spv
2+
; RUN: llvm-spirv --spirv-print-report %t.spv | FileCheck %s --check-prefix=CHECK-DAG
3+
4+
; CHECK-DAG: Version: 1.0
5+
; CHECK-DAG: Memory model: OpenCL
6+
; CHECK-DAG: Addressing model: Physical32
7+
; CHECK-DAG: Number of capabilities: 4
8+
; CHECK-DAG: Capability: Addresses
9+
; CHECK-DAG: Capability: Kernel
10+
; CHECK-DAG: Capability: LoopFuseINTEL
11+
; CHECK-DAG: Capability: BitInstructions
12+
; CHECK-DAG: Number of extensions: 2
13+
; CHECK-DAG: Extension: SPV_INTEL_loop_fuse
14+
; CHECK-DAG: Extension: SPV_KHR_bit_instructions
15+
; CHECK-DAG: Number of extended instruction sets: 1
16+
; CHECK-DAG: Extended Instruction Set: OpenCL.std
17+
18+
119734787 65536 393230 10 0
19+
2 Capability Addresses
20+
2 Capability Kernel
21+
2 Capability LoopFuseINTEL
22+
2 Capability BitInstructions
23+
6 Extension "SPV_INTEL_loop_fuse"
24+
8 Extension "SPV_KHR_bit_instructions"
25+
5 ExtInstImport 1 "OpenCL.std"
26+
3 MemoryModel 1 2
27+
7 EntryPoint 6 5 "TestSatPacked"
28+
3 Source 3 102000
29+
30+
5 Decorate 5 FuseLoopsInFunctionINTEL 3 1
31+
4 TypeInt 3 32 0
32+
2 TypeVoid 2
33+
5 TypeFunction 4 2 3 3
34+
35+
5 Function 2 5 0 4
36+
3 FunctionParameter 3 6
37+
3 FunctionParameter 3 7
38+
39+
2 Label 8
40+
4 BitReverse 3 9 6
41+
1 Return
42+
43+
1 FunctionEnd

llvm-spirv/tools/llvm-spirv/llvm-spirv.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,12 @@ static cl::opt<bool> SpecConstInfo(
196196
cl::desc("Display id of constants available for specializaion and their "
197197
"size in bytes"));
198198

199+
static cl::opt<bool>
200+
SPIRVPrintReport("spirv-print-report", cl::init(false),
201+
cl::desc("Display general information about the module "
202+
"(capabilities, extensions, version, memory model"
203+
" and addressing model)"));
204+
199205
static cl::opt<SPIRV::FPContractMode> FPCMode(
200206
"spirv-fp-contract", cl::desc("Set FP Contraction mode:"),
201207
cl::init(SPIRV::FPContractMode::On),
@@ -797,7 +803,7 @@ int main(int Ac, char **Av) {
797803
return convertSPIRV();
798804
#endif
799805

800-
if (!IsReverse && !IsRegularization && !SpecConstInfo)
806+
if (!IsReverse && !IsRegularization && !SpecConstInfo && !SPIRVPrintReport)
801807
return convertLLVMToSPIRV(Opts);
802808

803809
if (IsReverse && IsRegularization) {
@@ -824,5 +830,39 @@ int main(int Ac, char **Av) {
824830
<< ", size in bytes = " << SpecConst.Size
825831
<< ", type = " << SpecConst.Type << "\n";
826832
}
833+
834+
if (SPIRVPrintReport) {
835+
std::ifstream IFS(InputFile, std::ios::binary);
836+
int ErrCode = 0;
837+
std::optional<SPIRV::SPIRVModuleReport> BinReport =
838+
SPIRV::getSpirvReport(IFS, ErrCode);
839+
if (!BinReport) {
840+
std::cerr << "Invalid SPIR-V binary, error code is " << ErrCode << "\n";
841+
return -1;
842+
}
843+
844+
SPIRV::SPIRVModuleTextReport TextReport =
845+
SPIRV::formatSpirvReport(BinReport.value());
846+
847+
std::cout << "SPIR-V module report:"
848+
<< "\n Version: " << TextReport.Version
849+
<< "\n Memory model: " << TextReport.MemoryModel
850+
<< "\n Addressing model: " << TextReport.AddrModel << "\n";
851+
852+
std::cout << " Number of capabilities: " << TextReport.Capabilities.size()
853+
<< "\n";
854+
for (auto &Capability : TextReport.Capabilities)
855+
std::cout << " Capability: " << Capability << "\n";
856+
857+
std::cout << " Number of extensions: " << TextReport.Extensions.size()
858+
<< "\n";
859+
for (auto &Extension : TextReport.Extensions)
860+
std::cout << " Extension: " << Extension << "\n";
861+
862+
std::cout << " Number of extended instruction sets: "
863+
<< TextReport.ExtendedInstructionSets.size() << "\n";
864+
for (auto &ExtendedInstructionSet : TextReport.ExtendedInstructionSets)
865+
std::cout << " Extended Instruction Set: " << ExtendedInstructionSet << "\n";
866+
}
827867
return 0;
828868
}

0 commit comments

Comments
 (0)