Add IrBuilder for constructing SPIR-V in-memory representation.

This commit is contained in:
Lei Zhang 2016-05-22 14:17:39 -04:00
parent 200e897887
commit 8590f9cc81
10 changed files with 621 additions and 24 deletions

View File

@ -2,11 +2,13 @@ add_library(SPIRV-Tools-opt
basic_block.h
function.h
instruction.h
ir_loader.h
module.h
reflect.h
function.cpp
instruction.cpp
ir_loader.cpp
module.cpp
)

123
source/opt/ir_loader.cpp Normal file
View File

@ -0,0 +1,123 @@
// Copyright (c) 2016 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
// https://www.khronos.org/registry/
//
// THE MATERIALS ARE 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 AUTHORS 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
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#include "ir_loader.h"
#include <cassert>
#include "reflect.h"
namespace spvtools {
namespace opt {
namespace ir {
void IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
const auto opcode = static_cast<SpvOp>(inst->opcode);
if (IsDebugLineInst(opcode)) {
dbg_line_info_.push_back(Instruction(*inst));
return;
}
Instruction spv_inst(*inst, std::move(dbg_line_info_));
dbg_line_info_.clear();
// Handle function and basic block boundaries first, then normal
// instructions.
if (opcode == SpvOpFunction) {
assert(function_ == nullptr);
assert(block_ == nullptr);
function_.reset(new Function(std::move(spv_inst)));
} else if (opcode == SpvOpFunctionEnd) {
assert(function_ != nullptr);
assert(block_ == nullptr);
module_->AddFunction(std::move(*function_.release()));
function_ = nullptr;
} else if (opcode == SpvOpLabel) {
assert(function_ != nullptr);
assert(block_ == nullptr);
block_.reset(new BasicBlock(std::move(spv_inst)));
} else if (IsTerminatorInst(opcode)) {
assert(function_ != nullptr);
assert(block_ != nullptr);
block_->AddInstruction(std::move(spv_inst));
function_->AddBasicBlock(std::move(*block_.release()));
block_ = nullptr;
} else {
if (function_ == nullptr) { // Outside function definition
assert(block_ == nullptr);
if (opcode == SpvOpCapability) {
module_->AddCapability(std::move(spv_inst));
} else if (opcode == SpvOpExtension) {
module_->AddExtension(std::move(spv_inst));
} else if (opcode == SpvOpExtInstImport) {
module_->AddExtInstImport(std::move(spv_inst));
} else if (opcode == SpvOpMemoryModel) {
module_->SetMemoryModel(std::move(spv_inst));
} else if (opcode == SpvOpEntryPoint) {
module_->AddEntryPoint(std::move(spv_inst));
} else if (opcode == SpvOpExecutionMode) {
module_->AddExecutionMode(std::move(spv_inst));
} else if (IsDebugInst(opcode)) {
module_->AddDebugInst(std::move(spv_inst));
} else if (IsAnnotationInst(opcode)) {
module_->AddAnnotationInst(std::move(spv_inst));
} else if (IsTypeInst(opcode)) {
module_->AddType(std::move(spv_inst));
} else if (IsConstantInst(opcode)) {
module_->AddConstant(std::move(spv_inst));
} else if (opcode == SpvOpVariable) {
module_->AddGlobalVariable(std::move(spv_inst));
} else {
assert(0 && "unhandled inst type outside function defintion");
}
} else {
if (block_ == nullptr) { // Inside function but outside blocks
assert(opcode == SpvOpFunctionParameter);
function_->AddParameter(std::move(spv_inst));
} else {
block_->AddInstruction(std::move(spv_inst));
}
}
}
}
// Resolves internal references among the module, functions, basic blocks, etc.
// This function should be called after adding all instructions.
//
// This concluding call is needed because the whole in memory representation is
// designed around rvalues and move semantics, which subject to pointer
// invalidation during module construction internally.
void IrLoader::EndModule() {
for (auto& function : module_->functions()) {
for (auto& bb : function.basic_blocks()) {
bb.SetParent(&function);
}
function.SetParent(module_);
}
}
} // namespace ir
} // namespace opt
} // namespace spvtools

83
source/opt/ir_loader.h Normal file
View File

@ -0,0 +1,83 @@
// Copyright (c) 2016 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
// https://www.khronos.org/registry/
//
// THE MATERIALS ARE 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 AUTHORS 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
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#ifndef LIBSPIRV_OPT_IR_LOADER_H_
#define LIBSPIRV_OPT_IR_LOADER_H_
#include <memory>
#include "basic_block.h"
#include "instruction.h"
#include "module.h"
#include "spirv-tools/libspirv.h"
namespace spvtools {
namespace opt {
namespace ir {
// Loader class for constructing SPIR-V in-memory IR representation. Methods in
// this class are designed to work with the interface for spvBinaryParse() in
// libspirv.h so that we can leverage the syntax checks implemented behind it.
//
// The user is expected to call SetModuleHeader() to fill in the module's
// header, and then AddInstruction() for each decoded instruction, and finally
// EndModule() to finalize the module. The instructions processed in sequence
// by AddInstruction() should comprise a valid SPIR-V module.
class IrLoader {
public:
// Instantiates a builder to construct the given |module| gradually.
IrLoader(Module* module) : module_(module) {}
// Sets the fields in the module's header to the given parameters.
void SetModuleHeader(uint32_t magic, uint32_t version, uint32_t generator,
uint32_t bound, uint32_t reserved) {
module_->SetHeader({magic, version, generator, bound, reserved});
}
// Adds an instruction to the module. This method will properly capture and
// store the data provided in |inst| so that |inst| is no longer needed after
// returning.
void AddInstruction(const spv_parsed_instruction_t* inst);
// Finalizes the module construction. This must be called after the module
// header has been set and all instructions have been added.
// Resolves internal bookkeeping.
void EndModule();
private:
// The module to be built.
Module* module_;
// The current Function under construction.
std::unique_ptr<Function> function_;
// The current BasicBlock under construction.
std::unique_ptr<BasicBlock> block_;
// Line related debug instructions accumulated thus far.
std::vector<Instruction> dbg_line_info_;
};
} // namespace ir
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_OPT_IR_LOADER_H_

View File

@ -33,9 +33,9 @@ namespace ir {
std::vector<Instruction*> Module::types() {
std::vector<Instruction*> insts;
for (uint32_t i = 0; i < types_and_constants_.size(); ++i) {
if (IsTypeInst(types_and_constants_[i].opcode()))
insts.push_back(&types_and_constants_[i]);
for (uint32_t i = 0; i < types_values_.size(); ++i) {
if (IsTypeInst(types_values_[i].opcode()))
insts.push_back(&types_values_[i]);
}
return insts;
};
@ -43,14 +43,13 @@ std::vector<Instruction*> Module::types() {
void Module::ForEachInst(const std::function<void(Instruction*)>& f) {
for (auto& i : capabilities_) f(&i);
for (auto& i : extensions_) f(&i);
for (auto& i : ext_inst_sets_) f(&i);
for (auto& i : ext_inst_imports_) f(&i);
f(&memory_model_);
for (auto& i : entry_points_) f(&i);
for (auto& i : execution_modes_) f(&i);
for (auto& i : debugs_) f(&i);
for (auto& i : annotations_) f(&i);
for (auto& i : types_and_constants_) f(&i);
for (auto& i : variables_) f(&i);
for (auto& i : types_values_) f(&i);
for (auto& i : functions_) i.ForEachInst(f);
}
@ -65,14 +64,13 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
// TODO(antiagainst): wow, looks like a duplication of the above.
for (const auto& c : capabilities_) c.ToBinary(binary, skip_nop);
for (const auto& e : extensions_) e.ToBinary(binary, skip_nop);
for (const auto& e : ext_inst_sets_) e.ToBinary(binary, skip_nop);
for (const auto& e : ext_inst_imports_) e.ToBinary(binary, skip_nop);
memory_model_.ToBinary(binary, skip_nop);
for (const auto& e : entry_points_) e.ToBinary(binary, skip_nop);
for (const auto& e : execution_modes_) e.ToBinary(binary, skip_nop);
for (const auto& d : debugs_) d.ToBinary(binary, skip_nop);
for (const auto& a : annotations_) a.ToBinary(binary, skip_nop);
for (const auto& t : types_and_constants_) t.ToBinary(binary, skip_nop);
for (const auto& v : variables_) v.ToBinary(binary, skip_nop);
for (const auto& t : types_values_) t.ToBinary(binary, skip_nop);
for (const auto& f : functions_) f.ToBinary(binary, skip_nop);
}

View File

@ -28,8 +28,8 @@
#define LIBSPIRV_OPT_MODULE_H_
#include <functional>
#include <vector>
#include <utility>
#include <vector>
#include "function.h"
#include "instruction.h"
@ -61,8 +61,8 @@ class Module {
// Appends an extension instruction to this module.
void AddExtension(Instruction&& e) { extensions_.push_back(std::move(e)); }
// Appends an extended instruction set instruction to this module.
void AddExtInstSet(Instruction&& e) {
ext_inst_sets_.push_back(std::move(e));
void AddExtInstImport(Instruction&& e) {
ext_inst_imports_.push_back(std::move(e));
}
// Appends a memory model instruction to this module.
void SetMemoryModel(Instruction&& m) { memory_model_ = std::move(m); }
@ -79,15 +79,13 @@ class Module {
annotations_.push_back(std::move(a));
}
// Appends a type-declaration instruction to this module.
void AddType(Instruction&& t) {
types_and_constants_.push_back(std::move(t));
}
void AddType(Instruction&& t) { types_values_.push_back(std::move(t)); }
// Appends a constant-creation instruction to this module.
void AddConstant(Instruction&& c) {
types_and_constants_.push_back(std::move(c));
void AddConstant(Instruction&& c) { types_values_.push_back(std::move(c)); }
// Appends a global variable-declaration instruction to this module.
void AddGlobalVariable(Instruction&& v) {
types_values_.push_back(std::move(v));
}
// Appends a variable-declaration instruction to this module.
void AddVariable(Instruction&& v) { variables_.push_back(std::move(v)); }
// Appends a function to this module.
void AddFunction(Function&& f) { functions_.push_back(std::move(f)); }
@ -115,16 +113,14 @@ class Module {
// Section 2.4 of the SPIR-V specification.
std::vector<Instruction> capabilities_;
std::vector<Instruction> extensions_;
std::vector<Instruction> ext_inst_sets_;
std::vector<Instruction> ext_inst_imports_;
Instruction memory_model_; // A module only has one memory model instruction.
std::vector<Instruction> entry_points_;
std::vector<Instruction> execution_modes_;
std::vector<Instruction> debugs_;
std::vector<Instruction> annotations_;
// Types and constants may depends on each other; thus they are grouped
// together.
std::vector<Instruction> types_and_constants_;
std::vector<Instruction> variables_;
// Type declarations, constants, and global variable declarations.
std::vector<Instruction> types_values_;
std::vector<Function> functions_;
};

View File

@ -49,6 +49,7 @@ function(add_spvtools_unittest)
endif()
target_include_directories(${ARG_TARGET} PRIVATE
${spirv-tools_SOURCE_DIR}
${spirv-tools_SOURCE_DIR}/include
${SPIRV_HEADER_INCLUDE_DIR}
${gtest_SOURCE_DIR}/include
${gmock_SOURCE_DIR}/include
@ -131,6 +132,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
TARGET UnitSPIRV
SRCS ${TEST_SOURCES}
LIBS ${SPIRV_TOOLS})
add_subdirectory(opt)
else()
message(STATUS "Did not find googletest, tests will not be built."
"To enable tests place googletest in '<spirv-dir>/external/googletest'.")

30
test/opt/CMakeLists.txt Normal file
View File

@ -0,0 +1,30 @@
# Copyright (c) 2016 Google Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and/or associated documentation files (the
# "Materials"), to deal in the Materials without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Materials, and to
# permit persons to whom the Materials are furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Materials.
#
# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
# KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
# SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
# https://www.khronos.org/registry/
#
# THE MATERIALS ARE 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 AUTHORS 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
# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
add_spvtools_unittest(TARGET ir_loader
SRCS test_ir_loader.cpp opt_test_common.cpp
LIBS SPIRV-Tools-opt ${SPIRV_TOOLS}
)

View File

@ -0,0 +1,116 @@
// Copyright (c) 2016 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
// https://www.khronos.org/registry/
//
// THE MATERIALS ARE 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 AUTHORS 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
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#include "opt_test_common.h"
#include <gtest/gtest.h>
#include "source/opt/ir_loader.h"
#include "spirv-tools/libspirv.h"
namespace spvtools {
namespace opt {
namespace {
// Sets the module header. Meets the interface requirement of spvBinaryParse().
spv_result_t SetSpvHeader(void* builder, spv_endianness_t, uint32_t magic,
uint32_t version, uint32_t generator,
uint32_t id_bound, uint32_t reserved) {
reinterpret_cast<ir::IrLoader*>(builder)->SetModuleHeader(
magic, version, generator, id_bound, reserved);
return SPV_SUCCESS;
};
// Processes a parsed instruction. Meets the interface requirement of
// spvBinaryParse().
spv_result_t SetSpvInst(void* builder, const spv_parsed_instruction_t* inst) {
reinterpret_cast<ir::IrLoader*>(builder)->AddInstruction(inst);
return SPV_SUCCESS;
};
} // annoymous namespace
// Assembles the given assembly |text| and returns the binary.
std::vector<uint32_t> Assemble(const std::string& text) {
spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
spv_binary binary = nullptr;
spv_diagnostic diagnostic = nullptr;
spv_result_t status =
spvTextToBinary(context, text.data(), text.size(), &binary, &diagnostic);
EXPECT_EQ(SPV_SUCCESS, status) << "assemble text to binary failed";
std::vector<uint32_t> result(binary->code, binary->code + binary->wordCount);
spvDiagnosticDestroy(diagnostic);
spvBinaryDestroy(binary);
spvContextDestroy(context);
return result;
}
// Disassembles the given SPIR-V |binary| and returns the assembly.
std::string Disassemble(const std::vector<uint32_t>& binary) {
spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
spv_text text = nullptr;
spv_diagnostic diagnostic = nullptr;
spv_result_t status =
spvBinaryToText(context, binary.data(), binary.size(),
SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, &text, &diagnostic);
EXPECT_EQ(SPV_SUCCESS, status) << "disassemble binary to text failed";
std::string result(text->str, text->str + text->length);
spvDiagnosticDestroy(diagnostic);
spvTextDestroy(text);
spvContextDestroy(context);
return result;
}
// Builds and returns a Module for the given SPIR-V |binary|.
std::unique_ptr<ir::Module> BuildModule(const std::vector<uint32_t>& binary) {
spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
spv_diagnostic diagnostic = nullptr;
std::unique_ptr<ir::Module> module(new ir::Module);
ir::IrLoader builder(module.get());
spv_result_t status =
spvBinaryParse(context, &builder, binary.data(), binary.size(),
SetSpvHeader, SetSpvInst, &diagnostic);
EXPECT_EQ(SPV_SUCCESS, status) << "build ir::Module from binary failed";
spvDiagnosticDestroy(diagnostic);
spvContextDestroy(context);
builder.EndModule();
return module;
}
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,54 @@
// Copyright (c) 2016 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
// https://www.khronos.org/registry/
//
// THE MATERIALS ARE 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 AUTHORS 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
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#ifndef LIBSPIRV_TEST_OPT_OPT_TEST_COMMON_H_
#define LIBSPIRV_TEST_OPT_OPT_TEST_COMMON_H_
#include <memory>
#include <string>
#include <vector>
#include "source/opt/module.h"
namespace spvtools {
namespace opt {
// TODO(antiagainst): expand and export these functions as the C++ interface in
// libspirv.hpp.
// Assembles the given assembly |text| and returns the binary.
std::vector<uint32_t> Assemble(const std::string& text);
// Disassembles the given SPIR-V |binary| and returns the assembly.
std::string Disassemble(const std::vector<uint32_t>& binary);
// Builds and returns a Module for the given SPIR-V |binary|.
std::unique_ptr<ir::Module> BuildModule(const std::vector<uint32_t>& binary);
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_TEST_OPT_OPT_TEST_COMMON_H_

193
test/opt/test_ir_loader.cpp Normal file
View File

@ -0,0 +1,193 @@
// Copyright (c) 2016 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
// https://www.khronos.org/registry/
//
// THE MATERIALS ARE 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 AUTHORS 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
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#include <gtest/gtest.h>
#include "opt_test_common.h"
namespace {
using namespace spvtools::opt;
TEST(IrBuilder, RoundTrip) {
// #version 310 es
// int add(int a, int b) { return a + b; }
// void main() { add(1, 2); }
const std::string text =
"OpCapability Shader\n"
"%1 = OpExtInstImport \"GLSL.std.450\"\n"
"OpMemoryModel Logical GLSL450\n"
"OpEntryPoint Vertex %2 \"main\"\n"
"OpSource ESSL 310\n"
"OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"\n"
"OpSourceExtension \"GL_GOOGLE_include_directive\"\n"
"OpName %2 \"main\"\n"
"OpName %3 \"add(i1;i1;\"\n"
"OpName %4 \"a\"\n"
"OpName %5 \"b\"\n"
"OpName %6 \"param\"\n"
"OpName %7 \"param\"\n"
"%8 = OpTypeVoid\n"
"%9 = OpTypeFunction %8\n"
"%10 = OpTypeInt 32 1\n"
"%11 = OpTypePointer Function %10\n"
"%12 = OpTypeFunction %10 %11 %11\n"
"%13 = OpConstant %10 1\n"
"%14 = OpConstant %10 2\n"
"%2 = OpFunction %8 None %9\n"
"%15 = OpLabel\n"
"%6 = OpVariable %11 Function\n"
"%7 = OpVariable %11 Function\n"
"OpStore %6 %13\n"
"OpStore %7 %14\n"
"%16 = OpFunctionCall %10 %3 %6 %7\n"
"OpReturn\n"
"OpFunctionEnd\n"
"%3 = OpFunction %10 None %12\n"
"%4 = OpFunctionParameter %11\n"
"%5 = OpFunctionParameter %11\n"
"%17 = OpLabel\n"
"%18 = OpLoad %10 %4\n"
"%19 = OpLoad %10 %5\n"
"%20 = OpIAdd %10 %18 %19\n"
"OpReturnValue %20\n"
"OpFunctionEnd\n";
std::unique_ptr<ir::Module> module = BuildModule(Assemble(text));
std::vector<uint32_t> binary;
module->ToBinary(&binary, /* skip_nop = */ false);
EXPECT_EQ(text, Disassemble(binary));
}
TEST(IrBuilder, KeepLineDebugInfo) {
// #version 310 es
// void main() {}
const std::string text =
"OpCapability Shader\n"
"%1 = OpExtInstImport \"GLSL.std.450\"\n"
"OpMemoryModel Logical GLSL450\n"
"OpEntryPoint Vertex %2 \"main\"\n"
"%3 = OpString \"minimal.vert\"\n"
"OpSource ESSL 310\n"
"OpName %2 \"main\"\n"
"OpLine %3 10 10\n"
"%4 = OpTypeVoid\n"
"OpLine %3 100 100\n"
"%5 = OpTypeFunction %4\n"
"%2 = OpFunction %4 None %5\n"
"OpLine %3 1 1\n"
"OpNoLine\n"
"OpLine %3 2 2\n"
"OpLine %3 3 3\n"
"%6 = OpLabel\n"
"OpLine %3 4 4\n"
"OpNoLine\n"
"OpReturn\n"
"OpFunctionEnd\n";
std::unique_ptr<ir::Module> module = BuildModule(Assemble(text));
std::vector<uint32_t> binary;
module->ToBinary(&binary, /* skip_nop = */ false);
EXPECT_EQ(text, Disassemble(binary));
}
TEST(IrBuilder, LocalGlobalVariables) {
// #version 310 es
//
// float gv1 = 10.;
// float gv2 = 100.;
//
// float f() {
// float lv1 = gv1 + gv2;
// float lv2 = gv1 * gv2;
// return lv1 / lv2;
// }
//
// void main() {
// float lv1 = gv1 - gv2;
// }
const std::string text =
"OpCapability Shader\n"
"%1 = OpExtInstImport \"GLSL.std.450\"\n"
"OpMemoryModel Logical GLSL450\n"
"OpEntryPoint Vertex %2 \"main\"\n"
"OpSource ESSL 310\n"
"OpName %2 \"main\"\n"
"OpName %3 \"f(\"\n"
"OpName %4 \"gv1\"\n"
"OpName %5 \"gv2\"\n"
"OpName %6 \"lv1\"\n"
"OpName %7 \"lv2\"\n"
"OpName %8 \"lv1\"\n"
"%9 = OpTypeVoid\n"
"%10 = OpTypeFunction %9\n"
"%11 = OpTypeFloat 32\n"
"%12 = OpTypeFunction %11\n"
"%13 = OpTypePointer Private %11\n"
"%4 = OpVariable %13 Private\n"
"%14 = OpConstant %11 10\n"
"%5 = OpVariable %13 Private\n"
"%15 = OpConstant %11 100\n"
"%16 = OpTypePointer Function %11\n"
"%2 = OpFunction %9 None %10\n"
"%17 = OpLabel\n"
"%8 = OpVariable %16 Function\n"
"OpStore %4 %14\n"
"OpStore %5 %15\n"
"%18 = OpLoad %11 %4\n"
"%19 = OpLoad %11 %5\n"
"%20 = OpFSub %11 %18 %19\n"
"OpStore %8 %20\n"
"OpReturn\n"
"OpFunctionEnd\n"
"%3 = OpFunction %11 None %12\n"
"%21 = OpLabel\n"
"%6 = OpVariable %16 Function\n"
"%7 = OpVariable %16 Function\n"
"%22 = OpLoad %11 %4\n"
"%23 = OpLoad %11 %5\n"
"%24 = OpFAdd %11 %22 %23\n"
"OpStore %6 %24\n"
"%25 = OpLoad %11 %4\n"
"%26 = OpLoad %11 %5\n"
"%27 = OpFMul %11 %25 %26\n"
"OpStore %7 %27\n"
"%28 = OpLoad %11 %6\n"
"%29 = OpLoad %11 %7\n"
"%30 = OpFDiv %11 %28 %29\n"
"OpReturnValue %30\n"
"OpFunctionEnd\n";
std::unique_ptr<ir::Module> module = BuildModule(Assemble(text));
std::vector<uint32_t> binary;
module->ToBinary(&binary, /* skip_nop = */ false);
EXPECT_EQ(text, Disassemble(binary));
}
} // anonymous namespace