mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 19:20:05 +00:00
Adding ostream operators for IR structures
* Added for Instruction, BasicBlock, Function and Module * Uses new disassembly functionality that can disassemble individual instructions * For debug use only (no caching is done) * Each output converts module to binary, parses and outputs an individual instruction * Added a test for whole module output * Disabling Microsoft checked iterator warnings * Updated check_copyright.py to accept 2018
This commit is contained in:
parent
eb0c73dad6
commit
672494da13
@ -85,7 +85,7 @@ if(${COMPILER_IS_LIKE_GNU})
|
|||||||
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror)
|
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror)
|
||||||
endif()
|
endif()
|
||||||
elseif(MSVC)
|
elseif(MSVC)
|
||||||
set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS /wd4800)
|
set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800)
|
||||||
|
|
||||||
if(${SPIRV_WERROR})
|
if(${SPIRV_WERROR})
|
||||||
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX)
|
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX)
|
||||||
|
@ -228,6 +228,7 @@ set(SPIRV_SOURCES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/binary.h
|
${CMAKE_CURRENT_SOURCE_DIR}/binary.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/cfa.h
|
${CMAKE_CURRENT_SOURCE_DIR}/cfa.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h
|
${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h
|
${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.h
|
${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.h
|
${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.h
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "assembly_grammar.h"
|
#include "assembly_grammar.h"
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
|
#include "disassemble.h"
|
||||||
#include "ext_inst.h"
|
#include "ext_inst.h"
|
||||||
#include "name_mapper.h"
|
#include "name_mapper.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
@ -352,6 +353,52 @@ spv_result_t DisassembleInstruction(
|
|||||||
return disassembler->HandleInstruction(*parsed_instruction);
|
return disassembler->HandleInstruction(*parsed_instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simple wrapper class to provide extra data necessary for targeted
|
||||||
|
// instruction disassembly.
|
||||||
|
class WrappedDisassembler {
|
||||||
|
public:
|
||||||
|
WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc)
|
||||||
|
: disassembler_(dis), inst_binary_(binary), word_count_(wc) {}
|
||||||
|
|
||||||
|
Disassembler* disassembler() { return disassembler_; }
|
||||||
|
const uint32_t* inst_binary() const { return inst_binary_; }
|
||||||
|
size_t word_count() const { return word_count_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Disassembler* disassembler_;
|
||||||
|
const uint32_t* inst_binary_;
|
||||||
|
const size_t word_count_;
|
||||||
|
};
|
||||||
|
|
||||||
|
spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian,
|
||||||
|
uint32_t /* magic */, uint32_t version,
|
||||||
|
uint32_t generator, uint32_t id_bound,
|
||||||
|
uint32_t schema) {
|
||||||
|
assert(user_data);
|
||||||
|
auto wrapped = static_cast<WrappedDisassembler*>(user_data);
|
||||||
|
return wrapped->disassembler()->HandleHeader(endian, version, generator,
|
||||||
|
id_bound, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
spv_result_t DisassembleTargetInstruction(
|
||||||
|
void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
|
||||||
|
assert(user_data);
|
||||||
|
auto wrapped = static_cast<WrappedDisassembler*>(user_data);
|
||||||
|
// Check if this is the instruction we want to disassemble.
|
||||||
|
if (wrapped->word_count() == parsed_instruction->num_words &&
|
||||||
|
std::equal(wrapped->inst_binary(),
|
||||||
|
wrapped->inst_binary() + wrapped->word_count(),
|
||||||
|
parsed_instruction->words)) {
|
||||||
|
// Found the target instruction. Disassemble it and signal that we should
|
||||||
|
// stop searching so we don't output the same instruction again.
|
||||||
|
if (auto error =
|
||||||
|
wrapped->disassembler()->HandleInstruction(*parsed_instruction))
|
||||||
|
return error;
|
||||||
|
return SPV_REQUESTED_TERMINATION;
|
||||||
|
}
|
||||||
|
return SPV_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
spv_result_t spvBinaryToText(const spv_const_context context,
|
spv_result_t spvBinaryToText(const spv_const_context context,
|
||||||
@ -386,3 +433,44 @@ spv_result_t spvBinaryToText(const spv_const_context context,
|
|||||||
|
|
||||||
return disassembler.SaveTextResult(pText);
|
return disassembler.SaveTextResult(pText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string spvtools::spvInstructionBinaryToText(const spv_target_env env,
|
||||||
|
const uint32_t* instCode,
|
||||||
|
const size_t instWordCount,
|
||||||
|
const uint32_t* code,
|
||||||
|
const size_t wordCount,
|
||||||
|
const uint32_t options) {
|
||||||
|
spv_context context = spvContextCreate(env);
|
||||||
|
const libspirv::AssemblyGrammar grammar(context);
|
||||||
|
if (!grammar.isValid()) {
|
||||||
|
spvContextDestroy(context);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate friendly names for Ids if requested.
|
||||||
|
std::unique_ptr<libspirv::FriendlyNameMapper> friendly_mapper;
|
||||||
|
libspirv::NameMapper name_mapper = libspirv::GetTrivialNameMapper();
|
||||||
|
if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) {
|
||||||
|
friendly_mapper.reset(
|
||||||
|
new libspirv::FriendlyNameMapper(context, code, wordCount));
|
||||||
|
name_mapper = friendly_mapper->GetNameMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now disassemble!
|
||||||
|
Disassembler disassembler(grammar, options, name_mapper);
|
||||||
|
WrappedDisassembler wrapped(&disassembler, instCode, instWordCount);
|
||||||
|
spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader,
|
||||||
|
DisassembleTargetInstruction, nullptr);
|
||||||
|
|
||||||
|
spv_text text = nullptr;
|
||||||
|
std::string output;
|
||||||
|
if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) {
|
||||||
|
output.assign(text->str, text->str + text->length);
|
||||||
|
// Drop trailing newline characters.
|
||||||
|
while (!output.empty() && output.back() == '\n') output.pop_back();
|
||||||
|
}
|
||||||
|
spvTextDestroy(text);
|
||||||
|
spvContextDestroy(context);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
38
source/disassemble.h
Normal file
38
source/disassemble.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2018 Google Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef SPIRV_TOOLS_DISASSEMBLE_H_
|
||||||
|
#define SPIRV_TOOLS_DISASSEMBLE_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "spirv-tools/libspirv.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
|
||||||
|
// Decodes the given SPIR-V instruction binary representation to its assembly
|
||||||
|
// text. The context is inferred from the provided module binary. The options
|
||||||
|
// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will
|
||||||
|
// be stored into *text. Any error will be written into *diagnostic if
|
||||||
|
// diagnostic is non-null.
|
||||||
|
std::string spvInstructionBinaryToText(const spv_target_env env,
|
||||||
|
const uint32_t* inst_binary,
|
||||||
|
const size_t inst_word_count,
|
||||||
|
const uint32_t* binary,
|
||||||
|
const size_t word_count,
|
||||||
|
const uint32_t options);
|
||||||
|
|
||||||
|
} // namespace spvtools
|
||||||
|
|
||||||
|
#endif // SPIRV_TOOLS_DISASSEMBLE_H_
|
@ -15,9 +15,12 @@
|
|||||||
#include "basic_block.h"
|
#include "basic_block.h"
|
||||||
#include "function.h"
|
#include "function.h"
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
|
#include "reflect.h"
|
||||||
|
|
||||||
#include "make_unique.h"
|
#include "make_unique.h"
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
namespace spvtools {
|
namespace spvtools {
|
||||||
namespace ir {
|
namespace ir {
|
||||||
|
|
||||||
@ -155,5 +158,15 @@ uint32_t BasicBlock::ContinueBlockIdIfAny() const {
|
|||||||
return cbid;
|
return cbid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& str, const BasicBlock& block) {
|
||||||
|
block.ForEachInst([&str](const ir::Instruction* inst) {
|
||||||
|
str << *inst;
|
||||||
|
if (!IsTerminatorInst(inst->opcode())) {
|
||||||
|
str << std::endl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ir
|
} // namespace ir
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
@ -148,6 +148,7 @@ class BasicBlock {
|
|||||||
|
|
||||||
// Returns the terminator instruction. Assumes the terminator exists.
|
// Returns the terminator instruction. Assumes the terminator exists.
|
||||||
Instruction* terminator() { return &*tail(); }
|
Instruction* terminator() { return &*tail(); }
|
||||||
|
const Instruction* terminator() const { return &*ctail(); }
|
||||||
|
|
||||||
// Returns true if this basic block exits this function and returns to its
|
// Returns true if this basic block exits this function and returns to its
|
||||||
// caller.
|
// caller.
|
||||||
@ -165,6 +166,9 @@ class BasicBlock {
|
|||||||
InstructionList insts_;
|
InstructionList insts_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Pretty-prints |block| to |str|. Returns |str|.
|
||||||
|
std::ostream& operator<<(std::ostream& str, const BasicBlock& block);
|
||||||
|
|
||||||
inline BasicBlock::BasicBlock(std::unique_ptr<Instruction> label)
|
inline BasicBlock::BasicBlock(std::unique_ptr<Instruction> label)
|
||||||
: function_(nullptr), label_(std::move(label)) {}
|
: function_(nullptr), label_(std::move(label)) {}
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
#include "make_unique.h"
|
#include "make_unique.h"
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
namespace spvtools {
|
namespace spvtools {
|
||||||
namespace ir {
|
namespace ir {
|
||||||
|
|
||||||
@ -75,5 +77,15 @@ void Function::ForEachParam(const std::function<void(const Instruction*)>& f,
|
|||||||
->ForEachInst(f, run_on_debug_line_insts);
|
->ForEachInst(f, run_on_debug_line_insts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& str, const Function& func) {
|
||||||
|
func.ForEachInst([&str](const ir::Instruction* inst) {
|
||||||
|
str << *inst;
|
||||||
|
if (inst->opcode() != SpvOpFunctionEnd) {
|
||||||
|
str << std::endl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ir
|
} // namespace ir
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
@ -112,6 +112,9 @@ class Function {
|
|||||||
std::unique_ptr<Instruction> end_inst_;
|
std::unique_ptr<Instruction> end_inst_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Pretty-prints |func| to |str|. Returns |str|.
|
||||||
|
std::ostream& operator<<(std::ostream& str, const Function& func);
|
||||||
|
|
||||||
inline Function::Function(std::unique_ptr<Instruction> def_inst)
|
inline Function::Function(std::unique_ptr<Instruction> def_inst)
|
||||||
: module_(nullptr), def_inst_(std::move(def_inst)), end_inst_() {}
|
: module_(nullptr), def_inst_(std::move(def_inst)), end_inst_() {}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
|
||||||
|
#include "disassemble.h"
|
||||||
#include "fold.h"
|
#include "fold.h"
|
||||||
#include "instruction.h"
|
#include "instruction.h"
|
||||||
#include "ir_context.h"
|
#include "ir_context.h"
|
||||||
@ -476,5 +477,27 @@ bool Instruction::IsFoldable() const {
|
|||||||
return opt::IsFoldableType(type);
|
return opt::IsFoldableType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Instruction::PrettyPrint(uint32_t options) const {
|
||||||
|
// Convert the module to binary.
|
||||||
|
std::vector<uint32_t> module_binary;
|
||||||
|
context()->module()->ToBinary(&module_binary, /* skip_nop = */ false);
|
||||||
|
|
||||||
|
// Convert the instruction to binary. This is used to identify the correct
|
||||||
|
// stream of words to output from the module.
|
||||||
|
std::vector<uint32_t> inst_binary;
|
||||||
|
ToBinaryWithoutAttachedDebugInsts(&inst_binary);
|
||||||
|
|
||||||
|
// Do not generate a header.
|
||||||
|
return spvInstructionBinaryToText(
|
||||||
|
context()->grammar().target_env(), inst_binary.data(), inst_binary.size(),
|
||||||
|
module_binary.data(), module_binary.size(),
|
||||||
|
options | SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst) {
|
||||||
|
str << inst.PrettyPrint();
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ir
|
} // namespace ir
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
@ -351,6 +351,15 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
|||||||
// Spec constant.
|
// Spec constant.
|
||||||
inline bool IsConstant() const;
|
inline bool IsConstant() const;
|
||||||
|
|
||||||
|
// Pretty-prints |inst|.
|
||||||
|
//
|
||||||
|
// Provides the disassembly of a specific instruction. Utilizes |inst|'s
|
||||||
|
// context to provide the correct interpretation of types, constants, etc.
|
||||||
|
//
|
||||||
|
// |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER
|
||||||
|
// is always added to |options|.
|
||||||
|
std::string PrettyPrint(uint32_t options = 0u) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Returns the total count of result type id and result id.
|
// Returns the total count of result type id and result id.
|
||||||
uint32_t TypeResultIdCount() const {
|
uint32_t TypeResultIdCount() const {
|
||||||
@ -388,6 +397,14 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
|||||||
friend InstructionList;
|
friend InstructionList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Pretty-prints |inst| to |str| and returns |str|.
|
||||||
|
//
|
||||||
|
// Provides the disassembly of a specific instruction. Utilizes |inst|'s context
|
||||||
|
// to provide the correct interpretation of types, constants, etc.
|
||||||
|
//
|
||||||
|
// Disassembly uses raw ids (not pretty printed names).
|
||||||
|
std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst);
|
||||||
|
|
||||||
inline bool Instruction::operator==(const Instruction& other) const {
|
inline bool Instruction::operator==(const Instruction& other) const {
|
||||||
return unique_id() == other.unique_id();
|
return unique_id() == other.unique_id();
|
||||||
}
|
}
|
||||||
|
@ -391,6 +391,9 @@ class IRContext {
|
|||||||
return feature_mgr_.get();
|
return feature_mgr_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the grammar for this context.
|
||||||
|
const libspirv::AssemblyGrammar& grammar() const { return grammar_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Builds the def-use manager from scratch, even if it was already valid.
|
// Builds the def-use manager from scratch, even if it was already valid.
|
||||||
void BuildDefUseManager() {
|
void BuildDefUseManager() {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
#include "operand.h"
|
#include "operand.h"
|
||||||
#include "reflect.h"
|
#include "reflect.h"
|
||||||
@ -158,5 +159,15 @@ uint32_t Module::GetExtInstImportId(const char* extstr) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& str, const Module& module) {
|
||||||
|
module.ForEachInst([&str](const ir::Instruction* inst) {
|
||||||
|
str << *inst;
|
||||||
|
if (inst->opcode() != SpvOpFunctionEnd) {
|
||||||
|
str << std::endl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ir
|
} // namespace ir
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
@ -273,6 +273,9 @@ class Module {
|
|||||||
std::vector<std::unique_ptr<Function>> functions_;
|
std::vector<std::unique_ptr<Function>> functions_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Pretty-prints |module| to |str|. Returns |str|.
|
||||||
|
std::ostream& operator<<(std::ostream& str, const Module& module);
|
||||||
|
|
||||||
inline void Module::AddCapability(std::unique_ptr<Instruction> c) {
|
inline void Module::AddCapability(std::unique_ptr<Instruction> c) {
|
||||||
capabilities_.push_back(std::move(c));
|
capabilities_.push_back(std::move(c));
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
@ -26,10 +27,10 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using ::testing::Eq;
|
||||||
using spvtest::GetIdBound;
|
using spvtest::GetIdBound;
|
||||||
using spvtools::ir::IRContext;
|
using spvtools::ir::IRContext;
|
||||||
using spvtools::ir::Module;
|
using spvtools::ir::Module;
|
||||||
using ::testing::Eq;
|
|
||||||
|
|
||||||
TEST(ModuleTest, SetIdBound) {
|
TEST(ModuleTest, SetIdBound) {
|
||||||
Module m;
|
Module m;
|
||||||
@ -46,7 +47,8 @@ TEST(ModuleTest, SetIdBound) {
|
|||||||
// Returns an IRContext owning the module formed by assembling the given text,
|
// Returns an IRContext owning the module formed by assembling the given text,
|
||||||
// then loading the result.
|
// then loading the result.
|
||||||
inline std::unique_ptr<IRContext> BuildModule(std::string text) {
|
inline std::unique_ptr<IRContext> BuildModule(std::string text) {
|
||||||
return spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
|
return spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
||||||
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ModuleTest, ComputeIdBound) {
|
TEST(ModuleTest, ComputeIdBound) {
|
||||||
@ -74,4 +76,68 @@ TEST(ModuleTest, ComputeIdBound) {
|
|||||||
->ComputeIdBound());
|
->ComputeIdBound());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ModuleTest, OstreamOperator) {
|
||||||
|
const std::string text = R"(OpCapability Shader
|
||||||
|
OpCapability Linkage
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpName %7 "restrict"
|
||||||
|
OpDecorate %8 Restrict
|
||||||
|
%9 = OpTypeVoid
|
||||||
|
%10 = OpTypeInt 32 0
|
||||||
|
%11 = OpTypeStruct %10 %10
|
||||||
|
%12 = OpTypePointer Function %10
|
||||||
|
%13 = OpTypePointer Function %11
|
||||||
|
%14 = OpConstant %10 0
|
||||||
|
%15 = OpConstant %10 1
|
||||||
|
%7 = OpTypeFunction %9
|
||||||
|
%1 = OpFunction %9 None %7
|
||||||
|
%2 = OpLabel
|
||||||
|
%8 = OpVariable %13 Function
|
||||||
|
%3 = OpAccessChain %12 %8 %14
|
||||||
|
%4 = OpLoad %10 %3
|
||||||
|
%5 = OpAccessChain %12 %8 %15
|
||||||
|
%6 = OpLoad %10 %5
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd)";
|
||||||
|
|
||||||
|
std::string s;
|
||||||
|
std::ostringstream str(s);
|
||||||
|
str << *BuildModule(text)->module();
|
||||||
|
EXPECT_EQ(text, str.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ModuleTest, OstreamOperatorInt64) {
|
||||||
|
const std::string text = R"(OpCapability Shader
|
||||||
|
OpCapability Linkage
|
||||||
|
OpCapability Int64
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpName %7 "restrict"
|
||||||
|
OpDecorate %5 Restrict
|
||||||
|
%9 = OpTypeVoid
|
||||||
|
%10 = OpTypeInt 64 0
|
||||||
|
%11 = OpTypeStruct %10 %10
|
||||||
|
%12 = OpTypePointer Function %10
|
||||||
|
%13 = OpTypePointer Function %11
|
||||||
|
%14 = OpConstant %10 0
|
||||||
|
%15 = OpConstant %10 1
|
||||||
|
%16 = OpConstant %10 4294967297
|
||||||
|
%7 = OpTypeFunction %9
|
||||||
|
%1 = OpFunction %9 None %7
|
||||||
|
%2 = OpLabel
|
||||||
|
%5 = OpVariable %12 Function
|
||||||
|
%6 = OpLoad %10 %5
|
||||||
|
OpSelectionMerge %3 None
|
||||||
|
OpSwitch %6 %3 4294967297 %4
|
||||||
|
%4 = OpLabel
|
||||||
|
OpBranch %3
|
||||||
|
%3 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd)";
|
||||||
|
|
||||||
|
std::string s;
|
||||||
|
std::ostringstream str(s);
|
||||||
|
str << *BuildModule(text)->module();
|
||||||
|
EXPECT_EQ(text, str.str());
|
||||||
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
@ -31,9 +31,9 @@ AUTHORS = ['The Khronos Group Inc.',
|
|||||||
'LunarG Inc.',
|
'LunarG Inc.',
|
||||||
'Google Inc.',
|
'Google Inc.',
|
||||||
'Pierre Moreau']
|
'Pierre Moreau']
|
||||||
CURRENT_YEAR='2017'
|
CURRENT_YEAR='2018'
|
||||||
|
|
||||||
YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017)'
|
YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2018)'
|
||||||
COPYRIGHT_RE = re.compile(
|
COPYRIGHT_RE = re.compile(
|
||||||
'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
|
'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user