Updated spirv-tools.
This commit is contained in:
parent
cf5bf3c0e5
commit
5907520a19
4
3rdparty/spirv-tools/Android.mk
vendored
4
3rdparty/spirv-tools/Android.mk
vendored
@ -117,6 +117,7 @@ SPVTOOLS_OPT_SRC_FILES := \
|
||||
source/opt/inline_exhaustive_pass.cpp \
|
||||
source/opt/inline_opaque_pass.cpp \
|
||||
source/opt/inst_bindless_check_pass.cpp \
|
||||
source/opt/inst_buff_addr_check_pass.cpp \
|
||||
source/opt/instruction.cpp \
|
||||
source/opt/instruction_list.cpp \
|
||||
source/opt/instrument_pass.cpp \
|
||||
@ -171,7 +172,8 @@ SPVTOOLS_OPT_SRC_FILES := \
|
||||
source/opt/upgrade_memory_model.cpp \
|
||||
source/opt/value_number_table.cpp \
|
||||
source/opt/vector_dce.cpp \
|
||||
source/opt/workaround1209.cpp
|
||||
source/opt/workaround1209.cpp \
|
||||
source/opt/wrap_opkill.cpp
|
||||
|
||||
# Locations of grammar files.
|
||||
#
|
||||
|
4
3rdparty/spirv-tools/BUILD.gn
vendored
4
3rdparty/spirv-tools/BUILD.gn
vendored
@ -535,6 +535,8 @@ static_library("spvtools_opt") {
|
||||
"source/opt/inline_pass.h",
|
||||
"source/opt/inst_bindless_check_pass.cpp",
|
||||
"source/opt/inst_bindless_check_pass.h",
|
||||
"source/opt/inst_buff_addr_check_pass.cpp",
|
||||
"source/opt/inst_buff_addr_check_pass.h",
|
||||
"source/opt/instruction.cpp",
|
||||
"source/opt/instruction.h",
|
||||
"source/opt/instruction_list.cpp",
|
||||
@ -650,6 +652,8 @@ static_library("spvtools_opt") {
|
||||
"source/opt/vector_dce.h",
|
||||
"source/opt/workaround1209.cpp",
|
||||
"source/opt/workaround1209.h",
|
||||
"source/opt/wrap_opkill.cpp",
|
||||
"source/opt/wrap_opkill.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
|
2
3rdparty/spirv-tools/CHANGES
vendored
2
3rdparty/spirv-tools/CHANGES
vendored
@ -18,7 +18,7 @@ v2019.4 2019-08-08
|
||||
- Handle nested breaks from switches. (#2624)
|
||||
- Optimizer: Handle array type with OpSpecConstantOp length (#2652)
|
||||
- Perform merge return with single return in loop. (#2714)
|
||||
- Add —preserve-bindings and —preserve-spec-constants (#2693)
|
||||
- Add --preserve-bindings and --preserve-spec-constants (#2693)
|
||||
- Remove Common Uniform Elimination Pass (#2731)
|
||||
- Allow ray tracing shaders in inst bindle check pass. (#2733)
|
||||
- Add pass to inject code for robust-buffer-access semantics (#2771)
|
||||
|
@ -1 +1 @@
|
||||
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-2-g95386f9e"
|
||||
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-16-g8336d192"
|
||||
|
@ -23,8 +23,9 @@
|
||||
// communicate with shaders instrumented by passes created by:
|
||||
//
|
||||
// CreateInstBindlessCheckPass
|
||||
// CreateInstBuffAddrCheckPass
|
||||
//
|
||||
// More detailed documentation of this routine can be found in optimizer.hpp
|
||||
// More detailed documentation of these routines can be found in optimizer.hpp
|
||||
|
||||
namespace spvtools {
|
||||
|
||||
@ -157,6 +158,12 @@ static const int kInst2BindlessUninitOutDescIndex = kInst2StageOutCnt + 1;
|
||||
static const int kInst2BindlessUninitOutUnused = kInst2StageOutCnt + 2;
|
||||
static const int kInst2BindlessUninitOutCnt = kInst2StageOutCnt + 3;
|
||||
|
||||
// A buffer address unalloc error will output the 64-bit pointer in
|
||||
// two 32-bit pieces, lower bits first.
|
||||
static const int kInst2BuffAddrUnallocOutDescPtrLo = kInst2StageOutCnt + 1;
|
||||
static const int kInst2BuffAddrUnallocOutDescPtrHi = kInst2StageOutCnt + 2;
|
||||
static const int kInst2BuffAddrUnallocOutCnt = kInst2StageOutCnt + 3;
|
||||
|
||||
// DEPRECATED
|
||||
static const int kInstBindlessOutDescIndex = kInstStageOutCnt + 1;
|
||||
static const int kInstBindlessOutDescBound = kInstStageOutCnt + 2;
|
||||
@ -171,6 +178,7 @@ static const int kInst2MaxOutCnt = kInst2StageOutCnt + 3;
|
||||
// These are the possible validation error codes.
|
||||
static const int kInstErrorBindlessBounds = 0;
|
||||
static const int kInstErrorBindlessUninit = 1;
|
||||
static const int kInstErrorBuffAddrUnallocRef = 2;
|
||||
|
||||
// Direct Input Buffer Offsets
|
||||
//
|
||||
@ -187,14 +195,16 @@ static const int kDebugInputDataOffset = 0;
|
||||
// These are the bindings for the different buffers which are
|
||||
// read or written by the instrumentation passes.
|
||||
//
|
||||
// This is the output buffer written by InstBindlessCheckPass
|
||||
// and possibly other future validations.
|
||||
// This is the output buffer written by InstBindlessCheckPass,
|
||||
// InstBuffAddrCheckPass, and possibly other future validations.
|
||||
static const int kDebugOutputBindingStream = 0;
|
||||
|
||||
// The binding for the input buffer read by InstBindlessCheckPass and
|
||||
// possibly other future validations.
|
||||
// The binding for the input buffer read by InstBindlessCheckPass.
|
||||
static const int kDebugInputBindingBindless = 1;
|
||||
|
||||
// The binding for the input buffer read by InstBuffAddrCheckPass.
|
||||
static const int kDebugInputBindingBuffAddr = 2;
|
||||
|
||||
// Bindless Validation Input Buffer Format
|
||||
//
|
||||
// An input buffer for bindless validation consists of a single array of
|
||||
@ -216,6 +226,31 @@ static const int kDebugInputBindlessOffsetReserved = 0;
|
||||
// Data[ Data[ s + kDebugInputBindlessOffsetLengths ] + b ]
|
||||
static const int kDebugInputBindlessOffsetLengths = 1;
|
||||
|
||||
// Buffer Device Address Input Buffer Format
|
||||
//
|
||||
// An input buffer for buffer device address validation consists of a single
|
||||
// array of unsigned 64-bit integers we will call Data[]. This array is
|
||||
// formatted as follows:
|
||||
//
|
||||
// At offset kDebugInputBuffAddrPtrOffset is a list of sorted valid buffer
|
||||
// addresses. The list is terminated with the address 0xffffffffffffffff.
|
||||
// If 0x0 is not a valid buffer address, this address is inserted at the
|
||||
// start of the list.
|
||||
//
|
||||
static const int kDebugInputBuffAddrPtrOffset = 1;
|
||||
//
|
||||
// At offset kDebugInputBuffAddrLengthOffset in Data[] is a single uint64 which
|
||||
// gives an offset to the start of the buffer length data. More
|
||||
// specifically, for a buffer whose pointer is located at input buffer offset
|
||||
// i, the length is located at:
|
||||
//
|
||||
// Data[ i - kDebugInputBuffAddrPtrOffset
|
||||
// + Data[ kDebugInputBuffAddrLengthOffset ] ]
|
||||
//
|
||||
// The length associated with the 0xffffffffffffffff address is zero. If
|
||||
// not a valid buffer, the length associated with the 0x0 address is zero.
|
||||
static const int kDebugInputBuffAddrLengthOffset = 0;
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_
|
||||
|
@ -729,6 +729,30 @@ Optimizer::PassToken CreateInstBindlessCheckPass(
|
||||
uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false,
|
||||
bool input_init_enable = false, uint32_t version = 1);
|
||||
|
||||
// Create a pass to instrument physical buffer address checking
|
||||
// This pass instruments all physical buffer address references to check that
|
||||
// all referenced bytes fall in a valid buffer. If the reference is
|
||||
// invalid, a record is written to the debug output buffer (if space allows)
|
||||
// and a null value is returned. This pass is designed to support buffer
|
||||
// address validation in the Vulkan validation layers.
|
||||
//
|
||||
// Dead code elimination should be run after this pass as the original,
|
||||
// potentially invalid code is not removed and could cause undefined behavior,
|
||||
// including crashes. Instruction simplification would likely also be
|
||||
// beneficial. It is also generally recommended that this pass (and all
|
||||
// instrumentation passes) be run after any legalization and optimization
|
||||
// passes. This will give better analysis for the instrumentation and avoid
|
||||
// potentially de-optimizing the instrument code, for example, inlining
|
||||
// the debug record output function throughout the module.
|
||||
//
|
||||
// The instrumentation will read and write buffers in debug
|
||||
// descriptor set |desc_set|. It will write |shader_id| in each output record
|
||||
// to identify the shader module which generated the record.
|
||||
// |version| specifies the output buffer record format.
|
||||
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
|
||||
uint32_t shader_id,
|
||||
uint32_t version = 2);
|
||||
|
||||
// Create a pass to upgrade to the VulkanKHR memory model.
|
||||
// This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR.
|
||||
// Additionally, it modifies memory, image, atomic and barrier operations to
|
||||
@ -795,6 +819,10 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass();
|
||||
// for the first index.
|
||||
Optimizer::PassToken CreateDescriptorScalarReplacementPass();
|
||||
|
||||
// Create a pass to replace all OpKill instruction with a function call to a
|
||||
// function that has a single OpKill. This allows more code to be inlined.
|
||||
Optimizer::PassToken CreateWrapOpKillPass();
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
|
||||
|
@ -55,6 +55,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
||||
inline_opaque_pass.h
|
||||
inline_pass.h
|
||||
inst_bindless_check_pass.h
|
||||
inst_buff_addr_check_pass.h
|
||||
instruction.h
|
||||
instruction_list.h
|
||||
instrument_pass.h
|
||||
@ -113,6 +114,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
||||
value_number_table.h
|
||||
vector_dce.h
|
||||
workaround1209.h
|
||||
wrap_opkill.h
|
||||
|
||||
aggressive_dead_code_elim_pass.cpp
|
||||
basic_block.cpp
|
||||
@ -157,6 +159,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
||||
inline_opaque_pass.cpp
|
||||
inline_pass.cpp
|
||||
inst_bindless_check_pass.cpp
|
||||
inst_buff_addr_check_pass.cpp
|
||||
instruction.cpp
|
||||
instruction_list.cpp
|
||||
instrument_pass.cpp
|
||||
@ -212,6 +215,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
||||
value_number_table.cpp
|
||||
vector_dce.cpp
|
||||
workaround1209.cpp
|
||||
wrap_opkill.cpp
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
|
@ -527,6 +527,9 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
|
||||
pointer_type->storage_class());
|
||||
uint32_t new_pointer_type_id =
|
||||
context()->get_type_mgr()->GetTypeInstruction(&pointerTy);
|
||||
if (new_pointer_type_id == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_pointer_type_id != use->type_id()) {
|
||||
return CanUpdateUses(use, new_pointer_type_id);
|
||||
@ -542,6 +545,9 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
|
||||
const analysis::Type* new_type =
|
||||
type_mgr->GetMemberType(type, access_chain);
|
||||
uint32_t new_type_id = type_mgr->GetTypeInstruction(new_type);
|
||||
if (new_type_id == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_type_id != use->type_id()) {
|
||||
return CanUpdateUses(use, new_type_id);
|
||||
|
20
3rdparty/spirv-tools/source/opt/desc_sroa.cpp
vendored
20
3rdparty/spirv-tools/source/opt/desc_sroa.cpp
vendored
@ -14,7 +14,7 @@
|
||||
|
||||
#include "source/opt/desc_sroa.h"
|
||||
|
||||
#include <source/util/string_utils.h>
|
||||
#include "source/util/string_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -248,6 +248,24 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
|
||||
context()->AddAnnotationInst(std::move(new_decoration));
|
||||
}
|
||||
|
||||
// Create a new OpName for the replacement variable.
|
||||
for (auto p : context()->GetNames(var->result_id())) {
|
||||
Instruction* name_inst = p.second;
|
||||
std::string name_str = utils::MakeString(name_inst->GetOperand(1).words);
|
||||
name_str += "[";
|
||||
name_str += utils::ToString(idx);
|
||||
name_str += "]";
|
||||
|
||||
std::unique_ptr<Instruction> new_name(new Instruction(
|
||||
context(), SpvOpName, 0, 0,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {id}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
|
||||
Instruction* new_name_inst = new_name.get();
|
||||
context()->AddDebug2Inst(std::move(new_name));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
@ -77,10 +77,6 @@ class GraphicsRobustAccessPass : public Pass {
|
||||
// type. Ignores any value bits beyond the width of the type.
|
||||
Instruction* GetValueForType(uint64_t value, const analysis::Integer* type);
|
||||
|
||||
// Returns the unsigned value for the given constant. Assumes it's at most
|
||||
// 64 bits wide.
|
||||
uint64_t GetUnsignedValueForConstant(const analysis::Constant* c);
|
||||
|
||||
// Converts an integer value to an unsigned wider integer type, using either
|
||||
// sign extension or zero extension. The new instruction is inserted
|
||||
// immediately before |before_inst|, and is analyzed for definitions and uses.
|
||||
|
427
3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp
vendored
Normal file
427
3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp
vendored
Normal file
@ -0,0 +1,427 @@
|
||||
// Copyright (c) 2019 The Khronos Group Inc.
|
||||
// Copyright (c) 2019 Valve Corporation
|
||||
// Copyright (c) 2019 LunarG 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.
|
||||
|
||||
#include "inst_buff_addr_check_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
uint32_t InstBuffAddrCheckPass::CloneOriginalReference(
|
||||
Instruction* ref_inst, InstructionBuilder* builder) {
|
||||
// Clone original ref with new result id (if load)
|
||||
assert(
|
||||
(ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) &&
|
||||
"unexpected ref");
|
||||
std::unique_ptr<Instruction> new_ref_inst(ref_inst->Clone(context()));
|
||||
uint32_t ref_result_id = ref_inst->result_id();
|
||||
uint32_t new_ref_id = 0;
|
||||
if (ref_result_id != 0) {
|
||||
new_ref_id = TakeNextId();
|
||||
new_ref_inst->SetResultId(new_ref_id);
|
||||
}
|
||||
// Register new reference and add to new block
|
||||
Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst));
|
||||
uid2offset_[added_inst->unique_id()] = uid2offset_[ref_inst->unique_id()];
|
||||
if (new_ref_id != 0)
|
||||
get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id);
|
||||
return new_ref_id;
|
||||
}
|
||||
|
||||
bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) {
|
||||
if (ref_inst->opcode() != SpvOpLoad && ref_inst->opcode() != SpvOpStore)
|
||||
return false;
|
||||
uint32_t ptr_id = ref_inst->GetSingleWordInOperand(0);
|
||||
analysis::DefUseManager* du_mgr = get_def_use_mgr();
|
||||
Instruction* ptr_inst = du_mgr->GetDef(ptr_id);
|
||||
if (ptr_inst->opcode() != SpvOpAccessChain) return false;
|
||||
uint32_t ptr_ty_id = ptr_inst->type_id();
|
||||
Instruction* ptr_ty_inst = du_mgr->GetDef(ptr_ty_id);
|
||||
if (ptr_ty_inst->GetSingleWordInOperand(0) !=
|
||||
SpvStorageClassPhysicalStorageBufferEXT)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ??
|
||||
void InstBuffAddrCheckPass::GenCheckCode(
|
||||
uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id,
|
||||
uint32_t stage_idx, Instruction* ref_inst,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
||||
BasicBlock* back_blk_ptr = &*new_blocks->back();
|
||||
InstructionBuilder builder(
|
||||
context(), back_blk_ptr,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
// Gen conditional branch on check_id. Valid branch generates original
|
||||
// reference. Invalid generates debug output and zero result (if needed).
|
||||
uint32_t merge_blk_id = TakeNextId();
|
||||
uint32_t valid_blk_id = TakeNextId();
|
||||
uint32_t invalid_blk_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
|
||||
std::unique_ptr<Instruction> valid_label(NewLabel(valid_blk_id));
|
||||
std::unique_ptr<Instruction> invalid_label(NewLabel(invalid_blk_id));
|
||||
(void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id,
|
||||
merge_blk_id, SpvSelectionControlMaskNone);
|
||||
// Gen valid branch
|
||||
std::unique_ptr<BasicBlock> new_blk_ptr(
|
||||
new BasicBlock(std::move(valid_label)));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
uint32_t new_ref_id = CloneOriginalReference(ref_inst, &builder);
|
||||
(void)builder.AddBranch(merge_blk_id);
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
// Gen invalid block
|
||||
new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
// Convert uptr from uint64 to 2 uint32
|
||||
Instruction* lo_uptr_inst =
|
||||
builder.AddUnaryOp(GetUintId(), SpvOpUConvert, ref_uptr_id);
|
||||
Instruction* rshift_uptr_inst =
|
||||
builder.AddBinaryOp(GetUint64Id(), SpvOpShiftRightLogical, ref_uptr_id,
|
||||
builder.GetUintConstantId(32));
|
||||
Instruction* hi_uptr_inst = builder.AddUnaryOp(GetUintId(), SpvOpUConvert,
|
||||
rshift_uptr_inst->result_id());
|
||||
GenDebugStreamWrite(
|
||||
uid2offset_[ref_inst->unique_id()], stage_idx,
|
||||
{error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()},
|
||||
&builder);
|
||||
// Gen zero for invalid reference
|
||||
uint32_t ref_type_id = ref_inst->type_id();
|
||||
(void)builder.AddBranch(merge_blk_id);
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
// Gen merge block
|
||||
new_blk_ptr.reset(new BasicBlock(std::move(merge_label)));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
// Gen phi of new reference and zero, if necessary, and replace the
|
||||
// result id of the original reference with that of the Phi. Kill original
|
||||
// reference.
|
||||
if (new_ref_id != 0) {
|
||||
Instruction* phi_inst = builder.AddPhi(
|
||||
ref_type_id, {new_ref_id, valid_blk_id, builder.GetNullId(ref_type_id),
|
||||
invalid_blk_id});
|
||||
context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id());
|
||||
}
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
context()->KillInst(ref_inst);
|
||||
}
|
||||
|
||||
uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) {
|
||||
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
||||
switch (type_inst->opcode()) {
|
||||
case SpvOpTypeFloat:
|
||||
case SpvOpTypeInt:
|
||||
return type_inst->GetSingleWordInOperand(0) / 8u;
|
||||
case SpvOpTypeVector:
|
||||
case SpvOpTypeMatrix:
|
||||
return type_inst->GetSingleWordInOperand(1) *
|
||||
GetTypeLength(type_inst->GetSingleWordInOperand(0));
|
||||
case SpvOpTypePointer:
|
||||
assert(type_inst->GetSingleWordInOperand(0) ==
|
||||
SpvStorageClassPhysicalStorageBufferEXT &&
|
||||
"unexpected pointer type");
|
||||
return 8u;
|
||||
default:
|
||||
assert(false && "unexpected buffer reference type");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void InstBuffAddrCheckPass::AddParam(uint32_t type_id,
|
||||
std::vector<uint32_t>* param_vec,
|
||||
std::unique_ptr<Function>* input_func) {
|
||||
uint32_t pid = TakeNextId();
|
||||
param_vec->push_back(pid);
|
||||
std::unique_ptr<Instruction> param_inst(new Instruction(
|
||||
get_module()->context(), SpvOpFunctionParameter, type_id, pid, {}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst);
|
||||
(*input_func)->AddParameter(std::move(param_inst));
|
||||
}
|
||||
|
||||
uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() {
|
||||
if (search_test_func_id_ == 0) {
|
||||
// Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)"
|
||||
// which searches input buffer for buffer which most likely contains the
|
||||
// pointer value |ref_ptr| and verifies that the entire reference of
|
||||
// length |len| bytes is contained in the buffer.
|
||||
search_test_func_id_ = TakeNextId();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
std::vector<const analysis::Type*> param_types = {
|
||||
type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())};
|
||||
analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types);
|
||||
analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
|
||||
std::unique_ptr<Instruction> func_inst(
|
||||
new Instruction(get_module()->context(), SpvOpFunction, GetBoolId(),
|
||||
search_test_func_id_,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{SpvFunctionControlMaskNone}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{type_mgr->GetTypeInstruction(reg_func_ty)}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
|
||||
std::unique_ptr<Function> input_func =
|
||||
MakeUnique<Function>(std::move(func_inst));
|
||||
std::vector<uint32_t> param_vec;
|
||||
// Add ref_ptr and length parameters
|
||||
AddParam(GetUint64Id(), ¶m_vec, &input_func);
|
||||
AddParam(GetUintId(), ¶m_vec, &input_func);
|
||||
// Empty first block.
|
||||
uint32_t first_blk_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> first_blk_label(NewLabel(first_blk_id));
|
||||
std::unique_ptr<BasicBlock> first_blk_ptr =
|
||||
MakeUnique<BasicBlock>(std::move(first_blk_label));
|
||||
InstructionBuilder builder(
|
||||
context(), &*first_blk_ptr,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
uint32_t hdr_blk_id = TakeNextId();
|
||||
// Branch to search loop header
|
||||
std::unique_ptr<Instruction> hdr_blk_label(NewLabel(hdr_blk_id));
|
||||
(void)builder.AddInstruction(MakeUnique<Instruction>(
|
||||
context(), SpvOpBranch, 0, 0,
|
||||
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {hdr_blk_id}}}));
|
||||
first_blk_ptr->SetParent(&*input_func);
|
||||
input_func->AddBasicBlock(std::move(first_blk_ptr));
|
||||
// Linear search loop header block
|
||||
// TODO(greg-lunarg): Implement binary search
|
||||
std::unique_ptr<BasicBlock> hdr_blk_ptr =
|
||||
MakeUnique<BasicBlock>(std::move(hdr_blk_label));
|
||||
builder.SetInsertPoint(&*hdr_blk_ptr);
|
||||
// Phi for search index. Starts with 1.
|
||||
uint32_t cont_blk_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> cont_blk_label(NewLabel(cont_blk_id));
|
||||
// Deal with def-use cycle caused by search loop index computation.
|
||||
// Create Add and Phi instructions first, then do Def analysis on Add.
|
||||
// Add Phi and Add instructions and do Use analysis later.
|
||||
uint32_t idx_phi_id = TakeNextId();
|
||||
uint32_t idx_inc_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> idx_inc_inst(new Instruction(
|
||||
context(), SpvOpIAdd, GetUintId(), idx_inc_id,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{builder.GetUintConstantId(1u)}}}));
|
||||
std::unique_ptr<Instruction> idx_phi_inst(new Instruction(
|
||||
context(), SpvOpPhi, GetUintId(), idx_phi_id,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{builder.GetUintConstantId(1u)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst);
|
||||
// Add (previously created) search index phi
|
||||
(void)builder.AddInstruction(std::move(idx_phi_inst));
|
||||
// LoopMerge
|
||||
uint32_t bound_test_blk_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> bound_test_blk_label(
|
||||
NewLabel(bound_test_blk_id));
|
||||
(void)builder.AddInstruction(MakeUnique<Instruction>(
|
||||
context(), SpvOpLoopMerge, 0, 0,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {bound_test_blk_id}},
|
||||
{SPV_OPERAND_TYPE_ID, {cont_blk_id}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvLoopControlMaskNone}}}));
|
||||
// Branch to continue/work block
|
||||
(void)builder.AddInstruction(MakeUnique<Instruction>(
|
||||
context(), SpvOpBranch, 0, 0,
|
||||
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
|
||||
hdr_blk_ptr->SetParent(&*input_func);
|
||||
input_func->AddBasicBlock(std::move(hdr_blk_ptr));
|
||||
// Continue/Work Block. Read next buffer pointer and break if greater
|
||||
// than ref_ptr arg.
|
||||
std::unique_ptr<BasicBlock> cont_blk_ptr =
|
||||
MakeUnique<BasicBlock>(std::move(cont_blk_label));
|
||||
builder.SetInsertPoint(&*cont_blk_ptr);
|
||||
// Add (previously created) search index increment now.
|
||||
(void)builder.AddInstruction(std::move(idx_inc_inst));
|
||||
// Load next buffer address from debug input buffer
|
||||
uint32_t ibuf_id = GetInputBufferId();
|
||||
uint32_t ibuf_ptr_id = GetInputBufferPtrId();
|
||||
Instruction* uptr_ac_inst = builder.AddTernaryOp(
|
||||
ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
|
||||
builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id);
|
||||
uint32_t ibuf_type_id = GetInputBufferTypeId();
|
||||
Instruction* uptr_load_inst =
|
||||
builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, uptr_ac_inst->result_id());
|
||||
// If loaded address greater than ref_ptr arg, break, else branch back to
|
||||
// loop header
|
||||
Instruction* uptr_test_inst =
|
||||
builder.AddBinaryOp(GetBoolId(), SpvOpUGreaterThan,
|
||||
uptr_load_inst->result_id(), param_vec[0]);
|
||||
(void)builder.AddConditionalBranch(uptr_test_inst->result_id(),
|
||||
bound_test_blk_id, hdr_blk_id,
|
||||
kInvalidId, SpvSelectionControlMaskNone);
|
||||
cont_blk_ptr->SetParent(&*input_func);
|
||||
input_func->AddBasicBlock(std::move(cont_blk_ptr));
|
||||
// Bounds test block. Read length of selected buffer and test that
|
||||
// all len arg bytes are in buffer.
|
||||
std::unique_ptr<BasicBlock> bound_test_blk_ptr =
|
||||
MakeUnique<BasicBlock>(std::move(bound_test_blk_label));
|
||||
builder.SetInsertPoint(&*bound_test_blk_ptr);
|
||||
// Decrement index to point to previous/candidate buffer address
|
||||
Instruction* cand_idx_inst = builder.AddBinaryOp(
|
||||
GetUintId(), SpvOpISub, idx_inc_id, builder.GetUintConstantId(1u));
|
||||
// Load candidate buffer address
|
||||
Instruction* cand_ac_inst =
|
||||
builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
|
||||
builder.GetUintConstantId(kDebugInputDataOffset),
|
||||
cand_idx_inst->result_id());
|
||||
Instruction* cand_load_inst =
|
||||
builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, cand_ac_inst->result_id());
|
||||
// Compute offset of ref_ptr from candidate buffer address
|
||||
Instruction* offset_inst = builder.AddBinaryOp(
|
||||
ibuf_type_id, SpvOpISub, param_vec[0], cand_load_inst->result_id());
|
||||
// Convert ref length to uint64
|
||||
Instruction* ref_len_64_inst =
|
||||
builder.AddUnaryOp(ibuf_type_id, SpvOpUConvert, param_vec[1]);
|
||||
// Add ref length to ref offset to compute end of reference
|
||||
Instruction* ref_end_inst =
|
||||
builder.AddBinaryOp(ibuf_type_id, SpvOpIAdd, offset_inst->result_id(),
|
||||
ref_len_64_inst->result_id());
|
||||
// Load starting index of lengths in input buffer and convert to uint32
|
||||
Instruction* len_start_ac_inst =
|
||||
builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
|
||||
builder.GetUintConstantId(kDebugInputDataOffset),
|
||||
builder.GetUintConstantId(0u));
|
||||
Instruction* len_start_load_inst = builder.AddUnaryOp(
|
||||
ibuf_type_id, SpvOpLoad, len_start_ac_inst->result_id());
|
||||
Instruction* len_start_32_inst = builder.AddUnaryOp(
|
||||
GetUintId(), SpvOpUConvert, len_start_load_inst->result_id());
|
||||
// Decrement search index to get candidate buffer length index
|
||||
Instruction* cand_len_idx_inst =
|
||||
builder.AddBinaryOp(GetUintId(), SpvOpISub, cand_idx_inst->result_id(),
|
||||
builder.GetUintConstantId(1u));
|
||||
// Add candidate length index to start index
|
||||
Instruction* len_idx_inst = builder.AddBinaryOp(
|
||||
GetUintId(), SpvOpIAdd, cand_len_idx_inst->result_id(),
|
||||
len_start_32_inst->result_id());
|
||||
// Load candidate buffer length
|
||||
Instruction* len_ac_inst =
|
||||
builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
|
||||
builder.GetUintConstantId(kDebugInputDataOffset),
|
||||
len_idx_inst->result_id());
|
||||
Instruction* len_load_inst =
|
||||
builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, len_ac_inst->result_id());
|
||||
// Test if reference end within candidate buffer length
|
||||
Instruction* len_test_inst = builder.AddBinaryOp(
|
||||
GetBoolId(), SpvOpULessThanEqual, ref_end_inst->result_id(),
|
||||
len_load_inst->result_id());
|
||||
// Return test result
|
||||
(void)builder.AddInstruction(MakeUnique<Instruction>(
|
||||
context(), SpvOpReturnValue, 0, 0,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {len_test_inst->result_id()}}}));
|
||||
// Close block
|
||||
bound_test_blk_ptr->SetParent(&*input_func);
|
||||
input_func->AddBasicBlock(std::move(bound_test_blk_ptr));
|
||||
// Close function and add function to module
|
||||
std::unique_ptr<Instruction> func_end_inst(
|
||||
new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
|
||||
input_func->SetFunctionEnd(std::move(func_end_inst));
|
||||
context()->AddFunction(std::move(input_func));
|
||||
}
|
||||
return search_test_func_id_;
|
||||
}
|
||||
|
||||
uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst,
|
||||
InstructionBuilder* builder,
|
||||
uint32_t* ref_uptr_id) {
|
||||
// Enable Int64 if necessary
|
||||
if (!get_feature_mgr()->HasCapability(SpvCapabilityInt64)) {
|
||||
std::unique_ptr<Instruction> cap_int64_inst(new Instruction(
|
||||
context(), SpvOpCapability, 0, 0,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityInt64}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*cap_int64_inst);
|
||||
get_module()->AddCapability(std::move(cap_int64_inst));
|
||||
}
|
||||
// Convert reference pointer to uint64
|
||||
uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0);
|
||||
Instruction* ref_uptr_inst =
|
||||
builder->AddUnaryOp(GetUint64Id(), SpvOpConvertPtrToU, ref_ptr_id);
|
||||
*ref_uptr_id = ref_uptr_inst->result_id();
|
||||
// Compute reference length in bytes
|
||||
analysis::DefUseManager* du_mgr = get_def_use_mgr();
|
||||
Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id);
|
||||
uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id();
|
||||
Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id);
|
||||
uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1));
|
||||
uint32_t ref_len_id = builder->GetUintConstantId(ref_len);
|
||||
// Gen call to search and test function
|
||||
const std::vector<uint32_t> args = {GetSearchAndTestFuncId(), *ref_uptr_id,
|
||||
ref_len_id};
|
||||
Instruction* call_inst =
|
||||
builder->AddNaryOp(GetBoolId(), SpvOpFunctionCall, args);
|
||||
uint32_t retval = call_inst->result_id();
|
||||
return retval;
|
||||
}
|
||||
|
||||
void InstBuffAddrCheckPass::GenBuffAddrCheckCode(
|
||||
BasicBlock::iterator ref_inst_itr,
|
||||
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
||||
// Look for reference through indexed descriptor. If found, analyze and
|
||||
// save components. If not, return.
|
||||
Instruction* ref_inst = &*ref_inst_itr;
|
||||
if (!IsPhysicalBuffAddrReference(ref_inst)) return;
|
||||
// Move original block's preceding instructions into first new block
|
||||
std::unique_ptr<BasicBlock> new_blk_ptr;
|
||||
MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
|
||||
InstructionBuilder builder(
|
||||
context(), &*new_blk_ptr,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef);
|
||||
// Generate code to do search and test if all bytes of reference
|
||||
// are within a listed buffer. Return reference pointer converted to uint64.
|
||||
uint32_t ref_uptr_id;
|
||||
uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id);
|
||||
// Generate test of search results with true branch
|
||||
// being full reference and false branch being debug output and zero
|
||||
// for the referenced value.
|
||||
GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst,
|
||||
new_blocks);
|
||||
// Move original block's remaining code into remainder/merge block and add
|
||||
// to new blocks
|
||||
BasicBlock* back_blk_ptr = &*new_blocks->back();
|
||||
MovePostludeCode(ref_block_itr, back_blk_ptr);
|
||||
}
|
||||
|
||||
void InstBuffAddrCheckPass::InitInstBuffAddrCheck() {
|
||||
// Initialize base class
|
||||
InitializeInstrument();
|
||||
// Initialize class
|
||||
search_test_func_id_ = 0;
|
||||
}
|
||||
|
||||
Pass::Status InstBuffAddrCheckPass::ProcessImpl() {
|
||||
// Perform bindless bounds check on each entry point function in module
|
||||
InstProcessFunction pfn =
|
||||
[this](BasicBlock::iterator ref_inst_itr,
|
||||
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
||||
return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
|
||||
new_blocks);
|
||||
};
|
||||
bool modified = InstProcessEntryPointCallTree(pfn);
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
Pass::Status InstBuffAddrCheckPass::Process() {
|
||||
if (!get_feature_mgr()->HasCapability(
|
||||
SpvCapabilityPhysicalStorageBufferAddressesEXT))
|
||||
return Status::SuccessWithoutChange;
|
||||
InitInstBuffAddrCheck();
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
133
3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.h
vendored
Normal file
133
3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.h
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright (c) 2019 The Khronos Group Inc.
|
||||
// Copyright (c) 2019 Valve Corporation
|
||||
// Copyright (c) 2019 LunarG 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 LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_
|
||||
#define LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_
|
||||
|
||||
#include "instrument_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// This class/pass is designed to support the GPU-assisted validation layer of
|
||||
// the Buffer Device Address (BDA) extension in
|
||||
// https://github.com/KhronosGroup/Vulkan-ValidationLayers. The internal and
|
||||
// external design of this class may change as the layer evolves.
|
||||
class InstBuffAddrCheckPass : public InstrumentPass {
|
||||
public:
|
||||
// For test harness only
|
||||
InstBuffAddrCheckPass()
|
||||
: InstrumentPass(7, 23, kInstValidationIdBuffAddr, 1) {}
|
||||
// For all other interfaces
|
||||
InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version)
|
||||
: InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr,
|
||||
version) {}
|
||||
|
||||
~InstBuffAddrCheckPass() override = default;
|
||||
|
||||
// See optimizer.hpp for pass user documentation.
|
||||
Status Process() override;
|
||||
|
||||
const char* name() const override { return "inst-bindless-check-pass"; }
|
||||
|
||||
private:
|
||||
// Return byte length of type |type_id|. Must be int, float, vector, matrix
|
||||
// or physical pointer.
|
||||
uint32_t GetTypeLength(uint32_t type_id);
|
||||
|
||||
// Add |type_id| param to |input_func| and add id to |param_vec|.
|
||||
void AddParam(uint32_t type_id, std::vector<uint32_t>* param_vec,
|
||||
std::unique_ptr<Function>* input_func);
|
||||
|
||||
// Return id for search and test function. Generate it if not already gen'd.
|
||||
uint32_t GetSearchAndTestFuncId();
|
||||
|
||||
// Generate code into |builder| to do search of the BDA debug input buffer
|
||||
// for the buffer used by |ref_inst| and test that all bytes of reference
|
||||
// are within the buffer. Returns id of boolean value which is true if
|
||||
// search and test is successful, false otherwise.
|
||||
uint32_t GenSearchAndTest(Instruction* ref_inst, InstructionBuilder* builder,
|
||||
uint32_t* ref_uptr_id);
|
||||
|
||||
// This function does checking instrumentation on a single
|
||||
// instruction which references through a physical storage buffer address.
|
||||
// GenBuffAddrCheckCode generates code that checks that all bytes that
|
||||
// are referenced fall within a buffer that was queried via
|
||||
// the Vulkan API call vkGetBufferDeviceAddressEXT().
|
||||
//
|
||||
// The function is designed to be passed to
|
||||
// InstrumentPass::InstProcessEntryPointCallTree(), which applies the
|
||||
// function to each instruction in a module and replaces the instruction
|
||||
// with instrumented code if warranted.
|
||||
//
|
||||
// If |ref_inst_itr| is a physical storage buffer reference, return in
|
||||
// |new_blocks| the result of instrumenting it with validation code within
|
||||
// its block at |ref_block_itr|. The validation code first executes a check
|
||||
// for the specific condition called for. If the check passes, it executes
|
||||
// the remainder of the reference, otherwise writes a record to the debug
|
||||
// output buffer stream including |function_idx, instruction_idx, stage_idx|
|
||||
// and replaces the reference with the null value of the original type. The
|
||||
// block at |ref_block_itr| can just be replaced with the blocks in
|
||||
// |new_blocks|, which will contain at least two blocks. The last block will
|
||||
// comprise all instructions following |ref_inst_itr|,
|
||||
// preceded by a phi instruction if needed.
|
||||
//
|
||||
// This instrumentation function utilizes GenDebugStreamWrite() to write its
|
||||
// error records. The validation-specific part of the error record will
|
||||
// have the format:
|
||||
//
|
||||
// Validation Error Code (=kInstErrorBuffAddr)
|
||||
// Buffer Address (lowest 32 bits)
|
||||
// Buffer Address (highest 32 bits)
|
||||
//
|
||||
void GenBuffAddrCheckCode(
|
||||
BasicBlock::iterator ref_inst_itr,
|
||||
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
|
||||
|
||||
// Return true if |ref_inst| is a physical buffer address reference, false
|
||||
// otherwise.
|
||||
bool IsPhysicalBuffAddrReference(Instruction* ref_inst);
|
||||
|
||||
// Clone original reference |ref_inst| into |builder| and return id of result
|
||||
uint32_t CloneOriginalReference(Instruction* ref_inst,
|
||||
InstructionBuilder* builder);
|
||||
|
||||
// Generate instrumentation code for boolean test result |check_id|,
|
||||
// adding new blocks to |new_blocks|. Generate conditional branch to valid
|
||||
// or invalid reference blocks. Generate valid reference block which does
|
||||
// original reference |ref_inst|. Then generate invalid reference block which
|
||||
// writes debug error output utilizing |ref_inst|, |error_id| and
|
||||
// |stage_idx|. Generate merge block for valid and invalid reference blocks.
|
||||
// Kill original reference.
|
||||
void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t length_id,
|
||||
uint32_t stage_idx, Instruction* ref_inst,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
|
||||
|
||||
// Initialize state for instrumenting physical buffer address checking
|
||||
void InitInstBuffAddrCheck();
|
||||
|
||||
// Apply GenBuffAddrCheckCode to every instruction in module.
|
||||
Pass::Status ProcessImpl();
|
||||
|
||||
// Id of search and test function, if already gen'd, else zero.
|
||||
uint32_t search_test_func_id_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_
|
@ -107,7 +107,7 @@ void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id,
|
||||
builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id,
|
||||
builder->GetUintConstantId(field_offset));
|
||||
uint32_t buf_id = GetOutputBufferId();
|
||||
uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
|
||||
uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
|
||||
Instruction* achain_inst =
|
||||
builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
|
||||
builder->GetUintConstantId(kDebugOutputDataOffset),
|
||||
@ -373,19 +373,33 @@ void InstrumentPass::UpdateSucceedingPhis(
|
||||
});
|
||||
}
|
||||
|
||||
// Return id for output buffer uint ptr type
|
||||
uint32_t InstrumentPass::GetBufferUintPtrId() {
|
||||
if (buffer_uint_ptr_id_ == 0) {
|
||||
buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
|
||||
uint32_t InstrumentPass::GetOutputBufferPtrId() {
|
||||
if (output_buffer_ptr_id_ == 0) {
|
||||
output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
|
||||
GetUintId(), SpvStorageClassStorageBuffer);
|
||||
}
|
||||
return buffer_uint_ptr_id_;
|
||||
return output_buffer_ptr_id_;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetInputBufferTypeId() {
|
||||
return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id()
|
||||
: GetUintId();
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetInputBufferPtrId() {
|
||||
if (input_buffer_ptr_id_ == 0) {
|
||||
input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
|
||||
GetInputBufferTypeId(), SpvStorageClassStorageBuffer);
|
||||
}
|
||||
return input_buffer_ptr_id_;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetOutputBufferBinding() {
|
||||
switch (validation_id_) {
|
||||
case kInstValidationIdBindless:
|
||||
return kDebugOutputBindingStream;
|
||||
case kInstValidationIdBuffAddr:
|
||||
return kDebugOutputBindingStream;
|
||||
default:
|
||||
assert(false && "unexpected validation id");
|
||||
}
|
||||
@ -396,20 +410,24 @@ uint32_t InstrumentPass::GetInputBufferBinding() {
|
||||
switch (validation_id_) {
|
||||
case kInstValidationIdBindless:
|
||||
return kDebugInputBindingBindless;
|
||||
case kInstValidationIdBuffAddr:
|
||||
return kDebugInputBindingBuffAddr;
|
||||
default:
|
||||
assert(false && "unexpected validation id");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
analysis::Type* InstrumentPass::GetUintRuntimeArrayType(
|
||||
analysis::DecorationManager* deco_mgr, analysis::TypeManager* type_mgr) {
|
||||
if (uint_rarr_ty_ == nullptr) {
|
||||
analysis::Integer uint_ty(32, false);
|
||||
analysis::Type* InstrumentPass::GetUintXRuntimeArrayType(
|
||||
uint32_t width, analysis::Type** rarr_ty) {
|
||||
if (*rarr_ty == nullptr) {
|
||||
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::Integer uint_ty(width, false);
|
||||
analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
|
||||
analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty);
|
||||
uint_rarr_ty_ = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp);
|
||||
uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(uint_rarr_ty_);
|
||||
*rarr_ty = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp);
|
||||
uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(*rarr_ty);
|
||||
// By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of
|
||||
// a block, and will therefore be decorated with an ArrayStride. Therefore
|
||||
// the undecorated type returned here will not be pre-existing and can
|
||||
@ -418,9 +436,16 @@ analysis::Type* InstrumentPass::GetUintRuntimeArrayType(
|
||||
// invalidated after this pass.
|
||||
assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 &&
|
||||
"used RuntimeArray type returned");
|
||||
deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u);
|
||||
deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride,
|
||||
width / 8u);
|
||||
}
|
||||
return uint_rarr_ty_;
|
||||
return *rarr_ty;
|
||||
}
|
||||
|
||||
analysis::Type* InstrumentPass::GetUintRuntimeArrayType(uint32_t width) {
|
||||
analysis::Type** rarr_ty =
|
||||
(width == 64) ? &uint64_rarr_ty_ : &uint32_rarr_ty_;
|
||||
return GetUintXRuntimeArrayType(width, rarr_ty);
|
||||
}
|
||||
|
||||
void InstrumentPass::AddStorageBufferExt() {
|
||||
@ -445,8 +470,7 @@ uint32_t InstrumentPass::GetOutputBufferId() {
|
||||
// If not created yet, create one
|
||||
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::Type* reg_uint_rarr_ty =
|
||||
GetUintRuntimeArrayType(deco_mgr, type_mgr);
|
||||
analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(32);
|
||||
analysis::Integer uint_ty(32, false);
|
||||
analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
|
||||
analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty});
|
||||
@ -494,8 +518,8 @@ uint32_t InstrumentPass::GetInputBufferId() {
|
||||
// If not created yet, create one
|
||||
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::Type* reg_uint_rarr_ty =
|
||||
GetUintRuntimeArrayType(deco_mgr, type_mgr);
|
||||
uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u;
|
||||
analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width);
|
||||
analysis::Struct buf_ty({reg_uint_rarr_ty});
|
||||
analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty);
|
||||
uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
|
||||
@ -555,6 +579,16 @@ uint32_t InstrumentPass::GetUintId() {
|
||||
return uint_id_;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetUint64Id() {
|
||||
if (uint64_id_ == 0) {
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::Integer uint64_ty(64, false);
|
||||
analysis::Type* reg_uint64_ty = type_mgr->GetRegisteredType(&uint64_ty);
|
||||
uint64_id_ = type_mgr->GetTypeInstruction(reg_uint64_ty);
|
||||
}
|
||||
return uint64_id_;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetVecUintId(uint32_t len) {
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::Integer uint_ty(32, false);
|
||||
@ -642,7 +676,7 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
|
||||
(version_ == 1) ? kInstStageOutCnt : kInst2StageOutCnt;
|
||||
uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt;
|
||||
uint32_t buf_id = GetOutputBufferId();
|
||||
uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
|
||||
uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
|
||||
Instruction* obuf_curr_sz_ac_inst =
|
||||
builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
|
||||
builder.GetUintConstantId(kDebugOutputSizeOffset));
|
||||
@ -713,16 +747,17 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
|
||||
uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
|
||||
uint32_t func_id = param2input_func_id_[param_cnt];
|
||||
if (func_id != 0) return func_id;
|
||||
// Create input function for param_cnt
|
||||
// Create input function for param_cnt.
|
||||
func_id = TakeNextId();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
std::vector<const analysis::Type*> param_types;
|
||||
for (uint32_t c = 0; c < param_cnt; ++c)
|
||||
param_types.push_back(type_mgr->GetType(GetUintId()));
|
||||
analysis::Function func_ty(type_mgr->GetType(GetUintId()), param_types);
|
||||
uint32_t ibuf_type_id = GetInputBufferTypeId();
|
||||
analysis::Function func_ty(type_mgr->GetType(ibuf_type_id), param_types);
|
||||
analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
|
||||
std::unique_ptr<Instruction> func_inst(new Instruction(
|
||||
get_module()->context(), SpvOpFunction, GetUintId(), func_id,
|
||||
get_module()->context(), SpvOpFunction, ibuf_type_id, func_id,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{SpvFunctionControlMaskNone}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
@ -752,22 +787,27 @@ uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
|
||||
// loaded value if it exists, and load value from input buffer at new offset.
|
||||
// Return last loaded value.
|
||||
uint32_t buf_id = GetInputBufferId();
|
||||
uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
|
||||
uint32_t buf_ptr_id = GetInputBufferPtrId();
|
||||
uint32_t last_value_id = 0;
|
||||
for (uint32_t p = 0; p < param_cnt; ++p) {
|
||||
uint32_t offset_id;
|
||||
if (p == 0) {
|
||||
offset_id = param_vec[0];
|
||||
} else {
|
||||
if (ibuf_type_id != GetUintId()) {
|
||||
Instruction* ucvt_inst =
|
||||
builder.AddUnaryOp(GetUintId(), SpvOpUConvert, last_value_id);
|
||||
last_value_id = ucvt_inst->result_id();
|
||||
}
|
||||
Instruction* offset_inst = builder.AddBinaryOp(
|
||||
GetUintId(), SpvOpIAdd, last_value_id, param_vec[p]);
|
||||
offset_id = offset_inst->result_id();
|
||||
}
|
||||
Instruction* ac_inst = builder.AddTernaryOp(
|
||||
buf_uint_ptr_id, SpvOpAccessChain, buf_id,
|
||||
buf_ptr_id, SpvOpAccessChain, buf_id,
|
||||
builder.GetUintConstantId(kDebugInputDataOffset), offset_id);
|
||||
Instruction* load_inst =
|
||||
builder.AddUnaryOp(GetUintId(), SpvOpLoad, ac_inst->result_id());
|
||||
builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, ac_inst->result_id());
|
||||
last_value_id = load_inst->result_id();
|
||||
}
|
||||
(void)builder.AddInstruction(MakeUnique<Instruction>(
|
||||
@ -894,18 +934,21 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
|
||||
|
||||
void InstrumentPass::InitializeInstrument() {
|
||||
output_buffer_id_ = 0;
|
||||
buffer_uint_ptr_id_ = 0;
|
||||
output_buffer_ptr_id_ = 0;
|
||||
input_buffer_ptr_id_ = 0;
|
||||
output_func_id_ = 0;
|
||||
output_func_param_cnt_ = 0;
|
||||
input_buffer_id_ = 0;
|
||||
v4float_id_ = 0;
|
||||
uint_id_ = 0;
|
||||
uint64_id_ = 0;
|
||||
v4uint_id_ = 0;
|
||||
v3uint_id_ = 0;
|
||||
bool_id_ = 0;
|
||||
void_id_ = 0;
|
||||
storage_buffer_ext_defined_ = false;
|
||||
uint_rarr_ty_ = nullptr;
|
||||
uint32_rarr_ty_ = nullptr;
|
||||
uint64_rarr_ty_ = nullptr;
|
||||
|
||||
// clear collections
|
||||
id2function_.clear();
|
||||
|
@ -60,6 +60,7 @@ namespace opt {
|
||||
// These are used to identify the general validation being done and map to
|
||||
// its output buffers.
|
||||
static const uint32_t kInstValidationIdBindless = 0;
|
||||
static const uint32_t kInstValidationIdBuffAddr = 1;
|
||||
|
||||
class InstrumentPass : public Pass {
|
||||
using cbb_ptr = const BasicBlock*;
|
||||
@ -217,6 +218,9 @@ class InstrumentPass : public Pass {
|
||||
// Return id for 32-bit unsigned type
|
||||
uint32_t GetUintId();
|
||||
|
||||
// Return id for 32-bit unsigned type
|
||||
uint32_t GetUint64Id();
|
||||
|
||||
// Return id for 32-bit unsigned type
|
||||
uint32_t GetBoolId();
|
||||
|
||||
@ -224,11 +228,20 @@ class InstrumentPass : public Pass {
|
||||
uint32_t GetVoidId();
|
||||
|
||||
// Return pointer to type for runtime array of uint
|
||||
analysis::Type* GetUintRuntimeArrayType(analysis::DecorationManager* deco_mgr,
|
||||
analysis::TypeManager* type_mgr);
|
||||
analysis::Type* GetUintXRuntimeArrayType(uint32_t width,
|
||||
analysis::Type** rarr_ty);
|
||||
|
||||
// Return pointer to type for runtime array of uint
|
||||
analysis::Type* GetUintRuntimeArrayType(uint32_t width);
|
||||
|
||||
// Return id for buffer uint type
|
||||
uint32_t GetBufferUintPtrId();
|
||||
uint32_t GetOutputBufferPtrId();
|
||||
|
||||
// Return id for buffer uint type
|
||||
uint32_t GetInputBufferTypeId();
|
||||
|
||||
// Return id for buffer uint type
|
||||
uint32_t GetInputBufferPtrId();
|
||||
|
||||
// Return binding for output buffer for current validation.
|
||||
uint32_t GetOutputBufferBinding();
|
||||
@ -354,8 +367,11 @@ class InstrumentPass : public Pass {
|
||||
// id for output buffer variable
|
||||
uint32_t output_buffer_id_;
|
||||
|
||||
// type id for output buffer element
|
||||
uint32_t buffer_uint_ptr_id_;
|
||||
// ptr type id for output buffer element
|
||||
uint32_t output_buffer_ptr_id_;
|
||||
|
||||
// ptr type id for input buffer element
|
||||
uint32_t input_buffer_ptr_id_;
|
||||
|
||||
// id for debug output function
|
||||
uint32_t output_func_id_;
|
||||
@ -381,6 +397,9 @@ class InstrumentPass : public Pass {
|
||||
// id for 32-bit unsigned type
|
||||
uint32_t uint_id_;
|
||||
|
||||
// id for 32-bit unsigned type
|
||||
uint32_t uint64_id_;
|
||||
|
||||
// id for bool type
|
||||
uint32_t bool_id_;
|
||||
|
||||
@ -394,7 +413,10 @@ class InstrumentPass : public Pass {
|
||||
bool storage_buffer_ext_defined_;
|
||||
|
||||
// runtime array of uint type
|
||||
analysis::Type* uint_rarr_ty_;
|
||||
analysis::Type* uint64_rarr_ty_;
|
||||
|
||||
// runtime array of uint type
|
||||
analysis::Type* uint32_rarr_ty_;
|
||||
|
||||
// Pre-instrumentation same-block insts
|
||||
std::unordered_map<uint32_t, Instruction*> same_block_pre_;
|
||||
|
21
3rdparty/spirv-tools/source/opt/ir_builder.h
vendored
21
3rdparty/spirv-tools/source/opt/ir_builder.h
vendored
@ -465,6 +465,23 @@ class InstructionBuilder {
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
Instruction* AddFunctionCall(uint32_t result_type, uint32_t function,
|
||||
const std::vector<uint32_t>& parameters) {
|
||||
std::vector<Operand> operands;
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {function}});
|
||||
for (uint32_t id : parameters) {
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
|
||||
}
|
||||
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> new_inst(new Instruction(
|
||||
GetContext(), SpvOpFunctionCall, result_type, result_id, operands));
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
// Inserts the new instruction before the insertion point.
|
||||
Instruction* AddInstruction(std::unique_ptr<Instruction>&& insn) {
|
||||
Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));
|
||||
@ -512,6 +529,10 @@ class InstructionBuilder {
|
||||
|
||||
// Returns true if the users requested to update |analysis|.
|
||||
inline bool IsAnalysisUpdateRequested(IRContext::Analysis analysis) const {
|
||||
if (!GetContext()->AreAnalysesValid(analysis)) {
|
||||
// Do not try to update something that is not built.
|
||||
return false;
|
||||
}
|
||||
return preserved_analyses_ & analysis;
|
||||
}
|
||||
|
||||
|
34
3rdparty/spirv-tools/source/opt/optimizer.cpp
vendored
34
3rdparty/spirv-tools/source/opt/optimizer.cpp
vendored
@ -107,8 +107,10 @@ Optimizer& Optimizer::RegisterPass(PassToken&& p) {
|
||||
// or enable more copy propagation.
|
||||
Optimizer& Optimizer::RegisterLegalizationPasses() {
|
||||
return
|
||||
// Remove unreachable block so that merge return works.
|
||||
RegisterPass(CreateDeadBranchElimPass())
|
||||
// Wrap OpKill instructions so all other code can be inlined.
|
||||
RegisterPass(CreateWrapOpKillPass())
|
||||
// Remove unreachable block so that merge return works.
|
||||
.RegisterPass(CreateDeadBranchElimPass())
|
||||
// Merge the returns so we can inline.
|
||||
.RegisterPass(CreateMergeReturnPass())
|
||||
// Make sure uses and definitions are in the same function.
|
||||
@ -154,7 +156,8 @@ Optimizer& Optimizer::RegisterLegalizationPasses() {
|
||||
}
|
||||
|
||||
Optimizer& Optimizer::RegisterPerformancePasses() {
|
||||
return RegisterPass(CreateDeadBranchElimPass())
|
||||
return RegisterPass(CreateWrapOpKillPass())
|
||||
.RegisterPass(CreateDeadBranchElimPass())
|
||||
.RegisterPass(CreateMergeReturnPass())
|
||||
.RegisterPass(CreateInlineExhaustivePass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
@ -190,7 +193,8 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
|
||||
}
|
||||
|
||||
Optimizer& Optimizer::RegisterSizePasses() {
|
||||
return RegisterPass(CreateDeadBranchElimPass())
|
||||
return RegisterPass(CreateWrapOpKillPass())
|
||||
.RegisterPass(CreateDeadBranchElimPass())
|
||||
.RegisterPass(CreateMergeReturnPass())
|
||||
.RegisterPass(CreateInlineExhaustivePass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
@ -396,11 +400,20 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
|
||||
} else if (pass_name == "replace-invalid-opcode") {
|
||||
RegisterPass(CreateReplaceInvalidOpcodePass());
|
||||
} else if (pass_name == "inst-bindless-check") {
|
||||
RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2));
|
||||
RegisterPass(CreateSimplificationPass());
|
||||
RegisterPass(CreateDeadBranchElimPass());
|
||||
RegisterPass(CreateBlockMergePass());
|
||||
RegisterPass(CreateAggressiveDCEPass());
|
||||
} else if (pass_name == "inst-desc-idx-check") {
|
||||
RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true, 2));
|
||||
RegisterPass(CreateSimplificationPass());
|
||||
RegisterPass(CreateDeadBranchElimPass());
|
||||
RegisterPass(CreateBlockMergePass());
|
||||
RegisterPass(CreateAggressiveDCEPass());
|
||||
} else if (pass_name == "inst-buff-addr-check") {
|
||||
RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2));
|
||||
RegisterPass(CreateAggressiveDCEPass());
|
||||
} else if (pass_name == "simplify-instructions") {
|
||||
RegisterPass(CreateSimplificationPass());
|
||||
} else if (pass_name == "ssa-rewrite") {
|
||||
@ -477,6 +490,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
|
||||
RegisterPass(CreateDecomposeInitializedVariablesPass());
|
||||
} else if (pass_name == "graphics-robust-access") {
|
||||
RegisterPass(CreateGraphicsRobustAccessPass());
|
||||
} else if (pass_name == "wrap-opkill") {
|
||||
RegisterPass(CreateWrapOpKillPass());
|
||||
} else {
|
||||
Errorf(consumer(), nullptr, {},
|
||||
"Unknown flag '--%s'. Use --help for a list of valid flags",
|
||||
@ -853,6 +868,13 @@ Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
|
||||
input_init_enable, version));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
|
||||
uint32_t shader_id,
|
||||
uint32_t version) {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateCodeSinkingPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::CodeSinkingPass>());
|
||||
@ -893,4 +915,8 @@ Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
|
||||
MakeUnique<opt::DescriptorScalarReplacement>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateWrapOpKillPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
|
||||
}
|
||||
|
||||
} // namespace spvtools
|
||||
|
3
3rdparty/spirv-tools/source/opt/pass.cpp
vendored
3
3rdparty/spirv-tools/source/opt/pass.cpp
vendored
@ -43,7 +43,8 @@ Pass::Status Pass::Run(IRContext* ctx) {
|
||||
if (status == Status::SuccessWithChange) {
|
||||
ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses());
|
||||
}
|
||||
assert(ctx->IsConsistent());
|
||||
assert((status == Status::Failure || ctx->IsConsistent()) &&
|
||||
"An analysis in the context is out of date.");
|
||||
return status;
|
||||
}
|
||||
|
||||
|
2
3rdparty/spirv-tools/source/opt/passes.h
vendored
2
3rdparty/spirv-tools/source/opt/passes.h
vendored
@ -43,6 +43,7 @@
|
||||
#include "source/opt/inline_exhaustive_pass.h"
|
||||
#include "source/opt/inline_opaque_pass.h"
|
||||
#include "source/opt/inst_bindless_check_pass.h"
|
||||
#include "source/opt/inst_buff_addr_check_pass.h"
|
||||
#include "source/opt/legalize_vector_shuffle_pass.h"
|
||||
#include "source/opt/licm_pass.h"
|
||||
#include "source/opt/local_access_chain_convert_pass.h"
|
||||
@ -76,5 +77,6 @@
|
||||
#include "source/opt/upgrade_memory_model.h"
|
||||
#include "source/opt/vector_dce.h"
|
||||
#include "source/opt/workaround1209.h"
|
||||
#include "source/opt/wrap_opkill.h"
|
||||
|
||||
#endif // SOURCE_OPT_PASSES_H_
|
||||
|
@ -58,7 +58,9 @@ Pass::Status PrivateToLocalPass::Process() {
|
||||
|
||||
modified = !variables_to_move.empty();
|
||||
for (auto p : variables_to_move) {
|
||||
MoveVariable(p.first, p.second);
|
||||
if (!MoveVariable(p.first, p.second)) {
|
||||
return Status::Failure;
|
||||
}
|
||||
localized_variables.insert(p.first->result_id());
|
||||
}
|
||||
|
||||
@ -112,7 +114,7 @@ Function* PrivateToLocalPass::FindLocalFunction(const Instruction& inst) const {
|
||||
return target_function;
|
||||
} // namespace opt
|
||||
|
||||
void PrivateToLocalPass::MoveVariable(Instruction* variable,
|
||||
bool PrivateToLocalPass::MoveVariable(Instruction* variable,
|
||||
Function* function) {
|
||||
// The variable needs to be removed from the global section, and placed in the
|
||||
// header of the function. First step remove from the global list.
|
||||
@ -125,6 +127,9 @@ void PrivateToLocalPass::MoveVariable(Instruction* variable,
|
||||
|
||||
// Update the type as well.
|
||||
uint32_t new_type_id = GetNewType(variable->type_id());
|
||||
if (new_type_id == 0) {
|
||||
return false;
|
||||
}
|
||||
variable->SetResultType(new_type_id);
|
||||
|
||||
// Place the variable at the start of the first basic block.
|
||||
@ -133,7 +138,7 @@ void PrivateToLocalPass::MoveVariable(Instruction* variable,
|
||||
function->begin()->begin()->InsertBefore(move(var));
|
||||
|
||||
// Update uses where the type may have changed.
|
||||
UpdateUses(variable->result_id());
|
||||
return UpdateUses(variable->result_id());
|
||||
}
|
||||
|
||||
uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
|
||||
@ -143,7 +148,9 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
|
||||
old_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx);
|
||||
uint32_t new_type_id =
|
||||
type_mgr->FindPointerToType(pointee_type_id, SpvStorageClassFunction);
|
||||
context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id));
|
||||
if (new_type_id != 0) {
|
||||
context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id));
|
||||
}
|
||||
return new_type_id;
|
||||
}
|
||||
|
||||
@ -168,7 +175,7 @@ bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
|
||||
}
|
||||
}
|
||||
|
||||
void PrivateToLocalPass::UpdateUse(Instruction* inst) {
|
||||
bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
|
||||
// The cases in this switch have to match the cases in |IsValidUse|. If we
|
||||
// don't think it is valid, the optimization will not view the variable as a
|
||||
// candidate, and therefore the use will not be updated.
|
||||
@ -179,14 +186,20 @@ void PrivateToLocalPass::UpdateUse(Instruction* inst) {
|
||||
// The type is fine because it is the type pointed to, and that does not
|
||||
// change.
|
||||
break;
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpAccessChain: {
|
||||
context()->ForgetUses(inst);
|
||||
inst->SetResultType(GetNewType(inst->type_id()));
|
||||
uint32_t new_type_id = GetNewType(inst->type_id());
|
||||
if (new_type_id == 0) {
|
||||
return false;
|
||||
}
|
||||
inst->SetResultType(new_type_id);
|
||||
context()->AnalyzeUses(inst);
|
||||
|
||||
// Update uses where the type may have changed.
|
||||
UpdateUses(inst->result_id());
|
||||
break;
|
||||
if (!UpdateUses(inst->result_id())) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
case SpvOpName:
|
||||
case SpvOpEntryPoint: // entry points will be updated separately.
|
||||
break;
|
||||
@ -195,15 +208,20 @@ void PrivateToLocalPass::UpdateUse(Instruction* inst) {
|
||||
"Do not know how to update the type for this instruction.");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void PrivateToLocalPass::UpdateUses(uint32_t id) {
|
||||
|
||||
bool PrivateToLocalPass::UpdateUses(uint32_t id) {
|
||||
std::vector<Instruction*> uses;
|
||||
context()->get_def_use_mgr()->ForEachUser(
|
||||
id, [&uses](Instruction* use) { uses.push_back(use); });
|
||||
|
||||
for (Instruction* use : uses) {
|
||||
UpdateUse(use);
|
||||
if (!UpdateUse(use)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
|
@ -41,8 +41,8 @@ class PrivateToLocalPass : public Pass {
|
||||
|
||||
private:
|
||||
// Moves |variable| from the private storage class to the function storage
|
||||
// class of |function|.
|
||||
void MoveVariable(Instruction* variable, Function* function);
|
||||
// class of |function|. Returns false if the variable could not be moved.
|
||||
bool MoveVariable(Instruction* variable, Function* function);
|
||||
|
||||
// |inst| is an instruction declaring a varible. If that variable is
|
||||
// referenced in a single function and all of uses are valid as defined by
|
||||
@ -58,13 +58,13 @@ class PrivateToLocalPass : public Pass {
|
||||
// Given the result id of a pointer type, |old_type_id|, this function
|
||||
// returns the id of a the same pointer type except the storage class has
|
||||
// been changed to function. If the type does not already exist, it will be
|
||||
// created.
|
||||
// created. Returns 0 if the new type could not be found or generated.
|
||||
uint32_t GetNewType(uint32_t old_type_id);
|
||||
|
||||
// Updates |inst|, and any instruction dependent on |inst|, to reflect the
|
||||
// change of the base pointer now pointing to the function storage class.
|
||||
void UpdateUse(Instruction* inst);
|
||||
void UpdateUses(uint32_t id);
|
||||
bool UpdateUse(Instruction* inst);
|
||||
bool UpdateUses(uint32_t id);
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -78,36 +78,47 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
|
||||
}
|
||||
|
||||
std::vector<Instruction*> dead;
|
||||
if (get_def_use_mgr()->WhileEachUser(
|
||||
inst, [this, &replacements, &dead](Instruction* user) {
|
||||
if (!IsAnnotationInst(user->opcode())) {
|
||||
switch (user->opcode()) {
|
||||
case SpvOpLoad:
|
||||
ReplaceWholeLoad(user, replacements);
|
||||
dead.push_back(user);
|
||||
break;
|
||||
case SpvOpStore:
|
||||
ReplaceWholeStore(user, replacements);
|
||||
dead.push_back(user);
|
||||
break;
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
if (ReplaceAccessChain(user, replacements))
|
||||
dead.push_back(user);
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case SpvOpName:
|
||||
case SpvOpMemberName:
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unexpected opcode");
|
||||
break;
|
||||
bool replaced_all_uses = get_def_use_mgr()->WhileEachUser(
|
||||
inst, [this, &replacements, &dead](Instruction* user) {
|
||||
if (!IsAnnotationInst(user->opcode())) {
|
||||
switch (user->opcode()) {
|
||||
case SpvOpLoad:
|
||||
if (ReplaceWholeLoad(user, replacements)) {
|
||||
dead.push_back(user);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}))
|
||||
break;
|
||||
case SpvOpStore:
|
||||
if (ReplaceWholeStore(user, replacements)) {
|
||||
dead.push_back(user);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
if (ReplaceAccessChain(user, replacements))
|
||||
dead.push_back(user);
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case SpvOpName:
|
||||
case SpvOpMemberName:
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unexpected opcode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (replaced_all_uses) {
|
||||
dead.push_back(inst);
|
||||
} else {
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
// If there are no dead instructions to clean up, return with no changes.
|
||||
if (dead.empty()) return Status::SuccessWithoutChange;
|
||||
@ -133,7 +144,7 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
|
||||
return Status::SuccessWithChange;
|
||||
}
|
||||
|
||||
void ScalarReplacementPass::ReplaceWholeLoad(
|
||||
bool ScalarReplacementPass::ReplaceWholeLoad(
|
||||
Instruction* load, const std::vector<Instruction*>& replacements) {
|
||||
// Replaces the load of the entire composite with a load from each replacement
|
||||
// variable followed by a composite construction.
|
||||
@ -150,6 +161,9 @@ void ScalarReplacementPass::ReplaceWholeLoad(
|
||||
|
||||
Instruction* type = GetStorageType(var);
|
||||
uint32_t loadId = TakeNextId();
|
||||
if (loadId == 0) {
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<Instruction> newLoad(
|
||||
new Instruction(context(), SpvOpLoad, type->result_id(), loadId,
|
||||
std::initializer_list<Operand>{
|
||||
@ -168,6 +182,9 @@ void ScalarReplacementPass::ReplaceWholeLoad(
|
||||
|
||||
// Construct a new composite.
|
||||
uint32_t compositeId = TakeNextId();
|
||||
if (compositeId == 0) {
|
||||
return false;
|
||||
}
|
||||
where = load;
|
||||
std::unique_ptr<Instruction> compositeConstruct(new Instruction(
|
||||
context(), SpvOpCompositeConstruct, load->type_id(), compositeId, {}));
|
||||
@ -180,9 +197,10 @@ void ScalarReplacementPass::ReplaceWholeLoad(
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*where);
|
||||
context()->set_instr_block(&*where, block);
|
||||
context()->ReplaceAllUsesWith(load->result_id(), compositeId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScalarReplacementPass::ReplaceWholeStore(
|
||||
bool ScalarReplacementPass::ReplaceWholeStore(
|
||||
Instruction* store, const std::vector<Instruction*>& replacements) {
|
||||
// Replaces a store to the whole composite with a series of extract and stores
|
||||
// to each element.
|
||||
@ -199,6 +217,9 @@ void ScalarReplacementPass::ReplaceWholeStore(
|
||||
|
||||
Instruction* type = GetStorageType(var);
|
||||
uint32_t extractId = TakeNextId();
|
||||
if (extractId == 0) {
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<Instruction> extract(new Instruction(
|
||||
context(), SpvOpCompositeExtract, type->result_id(), extractId,
|
||||
std::initializer_list<Operand>{
|
||||
@ -224,6 +245,7 @@ void ScalarReplacementPass::ReplaceWholeStore(
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
|
||||
context()->set_instr_block(&*iter, block);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScalarReplacementPass::ReplaceAccessChain(
|
||||
@ -247,6 +269,9 @@ bool ScalarReplacementPass::ReplaceAccessChain(
|
||||
// Replace input access chain with another access chain.
|
||||
BasicBlock::iterator chainIter(chain);
|
||||
uint32_t replacementId = TakeNextId();
|
||||
if (replacementId == 0) {
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<Instruction> replacementChain(new Instruction(
|
||||
context(), chain->opcode(), chain->type_id(), replacementId,
|
||||
std::initializer_list<Operand>{
|
||||
@ -329,6 +354,10 @@ void ScalarReplacementPass::TransferAnnotations(
|
||||
if (decoration == SpvDecorationInvariant ||
|
||||
decoration == SpvDecorationRestrict) {
|
||||
for (auto var : *replacements) {
|
||||
if (var == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<Instruction> annotation(
|
||||
new Instruction(context(), SpvOpDecorate, 0, 0,
|
||||
std::initializer_list<Operand>{
|
||||
@ -350,6 +379,11 @@ void ScalarReplacementPass::CreateVariable(
|
||||
std::vector<Instruction*>* replacements) {
|
||||
uint32_t ptrId = GetOrCreatePointerType(typeId);
|
||||
uint32_t id = TakeNextId();
|
||||
|
||||
if (id == 0) {
|
||||
replacements->push_back(nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<Instruction> variable(new Instruction(
|
||||
context(), SpvOpVariable, ptrId, id,
|
||||
std::initializer_list<Operand>{
|
||||
@ -671,44 +705,51 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst) const {
|
||||
|
||||
bool ScalarReplacementPass::CheckUses(const Instruction* inst,
|
||||
VariableStats* stats) const {
|
||||
uint64_t max_legal_index = GetMaxLegalIndex(inst);
|
||||
|
||||
bool ok = true;
|
||||
get_def_use_mgr()->ForEachUse(
|
||||
inst, [this, stats, &ok](const Instruction* user, uint32_t index) {
|
||||
// Annotations are check as a group separately.
|
||||
if (!IsAnnotationInst(user->opcode())) {
|
||||
switch (user->opcode()) {
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
if (index == 2u && user->NumInOperands() > 1) {
|
||||
uint32_t id = user->GetSingleWordInOperand(1u);
|
||||
const Instruction* opInst = get_def_use_mgr()->GetDef(id);
|
||||
if (!IsCompileTimeConstantInst(opInst->opcode())) {
|
||||
ok = false;
|
||||
} else {
|
||||
if (!CheckUsesRelaxed(user)) ok = false;
|
||||
}
|
||||
stats->num_partial_accesses++;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case SpvOpLoad:
|
||||
if (!CheckLoad(user, index)) ok = false;
|
||||
stats->num_full_accesses++;
|
||||
break;
|
||||
case SpvOpStore:
|
||||
if (!CheckStore(user, index)) ok = false;
|
||||
stats->num_full_accesses++;
|
||||
break;
|
||||
case SpvOpName:
|
||||
case SpvOpMemberName:
|
||||
break;
|
||||
default:
|
||||
get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok](
|
||||
const Instruction* user,
|
||||
uint32_t index) {
|
||||
// Annotations are check as a group separately.
|
||||
if (!IsAnnotationInst(user->opcode())) {
|
||||
switch (user->opcode()) {
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
if (index == 2u && user->NumInOperands() > 1) {
|
||||
uint32_t id = user->GetSingleWordInOperand(1u);
|
||||
const Instruction* opInst = get_def_use_mgr()->GetDef(id);
|
||||
const auto* constant =
|
||||
context()->get_constant_mgr()->GetConstantFromInst(opInst);
|
||||
if (!constant) {
|
||||
ok = false;
|
||||
break;
|
||||
} else if (constant->GetZeroExtendedValue() >= max_legal_index) {
|
||||
ok = false;
|
||||
} else {
|
||||
if (!CheckUsesRelaxed(user)) ok = false;
|
||||
}
|
||||
stats->num_partial_accesses++;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case SpvOpLoad:
|
||||
if (!CheckLoad(user, index)) ok = false;
|
||||
stats->num_full_accesses++;
|
||||
break;
|
||||
case SpvOpStore:
|
||||
if (!CheckStore(user, index)) ok = false;
|
||||
stats->num_full_accesses++;
|
||||
break;
|
||||
case SpvOpName:
|
||||
case SpvOpMemberName:
|
||||
break;
|
||||
default:
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ok;
|
||||
}
|
||||
@ -838,5 +879,24 @@ Instruction* ScalarReplacementPass::CreateNullConstant(uint32_t type_id) {
|
||||
return null_inst;
|
||||
}
|
||||
|
||||
uint64_t ScalarReplacementPass::GetMaxLegalIndex(
|
||||
const Instruction* var_inst) const {
|
||||
assert(var_inst->opcode() == SpvOpVariable &&
|
||||
"|var_inst| must be a variable instruction.");
|
||||
Instruction* type = GetStorageType(var_inst);
|
||||
switch (type->opcode()) {
|
||||
case SpvOpTypeStruct:
|
||||
return type->NumInOperands();
|
||||
case SpvOpTypeArray:
|
||||
return GetArrayLength(type);
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector:
|
||||
return GetNumElements(type);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -143,7 +143,8 @@ class ScalarReplacementPass : public Pass {
|
||||
bool CheckStore(const Instruction* inst, uint32_t index) const;
|
||||
|
||||
// Creates a variable of type |typeId| from the |index|'th element of
|
||||
// |varInst|. The new variable is added to |replacements|.
|
||||
// |varInst|. The new variable is added to |replacements|. If the variable
|
||||
// could not be created, then |nullptr| is appended to |replacements|.
|
||||
void CreateVariable(uint32_t typeId, Instruction* varInst, uint32_t index,
|
||||
std::vector<Instruction*>* replacements);
|
||||
|
||||
@ -187,21 +188,21 @@ class ScalarReplacementPass : public Pass {
|
||||
// Generates a load for each replacement variable and then creates a new
|
||||
// composite by combining all of the loads.
|
||||
//
|
||||
// |load| must be a load.
|
||||
void ReplaceWholeLoad(Instruction* load,
|
||||
// |load| must be a load. Returns true if successful.
|
||||
bool ReplaceWholeLoad(Instruction* load,
|
||||
const std::vector<Instruction*>& replacements);
|
||||
|
||||
// Replaces the store to the entire composite.
|
||||
//
|
||||
// Generates a composite extract and store for each element in the scalarized
|
||||
// variable from the original store data input.
|
||||
void ReplaceWholeStore(Instruction* store,
|
||||
// variable from the original store data input. Returns true if successful.
|
||||
bool ReplaceWholeStore(Instruction* store,
|
||||
const std::vector<Instruction*>& replacements);
|
||||
|
||||
// Replaces an access chain to the composite variable with either a direct use
|
||||
// of the appropriate replacement variable or another access chain with the
|
||||
// replacement variable as the base and one fewer indexes. Returns false if
|
||||
// the chain has an out of bounds access.
|
||||
// replacement variable as the base and one fewer indexes. Returns true if
|
||||
// successful.
|
||||
bool ReplaceAccessChain(Instruction* chain,
|
||||
const std::vector<Instruction*>& replacements);
|
||||
|
||||
@ -222,10 +223,16 @@ class ScalarReplacementPass : public Pass {
|
||||
// Maps type id to OpConstantNull for that type.
|
||||
std::unordered_map<uint32_t, uint32_t> type_to_null_;
|
||||
|
||||
// Returns the number of elements in the variable |var_inst|.
|
||||
uint64_t GetMaxLegalIndex(const Instruction* var_inst) const;
|
||||
|
||||
// Returns true if |length| is larger than limit on the size of the variable
|
||||
// that we will be willing to split.
|
||||
bool IsLargerThanSizeLimit(uint64_t length) const;
|
||||
|
||||
// Limit on the number of members in an object that will be replaced.
|
||||
// 0 means there is no limit.
|
||||
uint32_t max_num_elements_;
|
||||
bool IsLargerThanSizeLimit(uint64_t length) const;
|
||||
char name_[55];
|
||||
};
|
||||
|
||||
|
47
3rdparty/spirv-tools/source/opt/type_manager.cpp
vendored
47
3rdparty/spirv-tools/source/opt/type_manager.cpp
vendored
@ -213,6 +213,10 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
std::unique_ptr<Instruction> typeInst;
|
||||
// TODO(1841): Handle id overflow.
|
||||
id = context()->TakeNextId();
|
||||
if (id == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
RegisterType(id, *type);
|
||||
switch (type->kind()) {
|
||||
#define DefineParameterlessCase(kind) \
|
||||
@ -247,6 +251,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
break;
|
||||
case Type::kVector: {
|
||||
uint32_t subtype = GetTypeInstruction(type->AsVector()->element_type());
|
||||
if (subtype == 0) {
|
||||
return 0;
|
||||
}
|
||||
typeInst =
|
||||
MakeUnique<Instruction>(context(), SpvOpTypeVector, 0, id,
|
||||
std::initializer_list<Operand>{
|
||||
@ -257,6 +264,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
}
|
||||
case Type::kMatrix: {
|
||||
uint32_t subtype = GetTypeInstruction(type->AsMatrix()->element_type());
|
||||
if (subtype == 0) {
|
||||
return 0;
|
||||
}
|
||||
typeInst =
|
||||
MakeUnique<Instruction>(context(), SpvOpTypeMatrix, 0, id,
|
||||
std::initializer_list<Operand>{
|
||||
@ -268,6 +278,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
case Type::kImage: {
|
||||
const Image* image = type->AsImage();
|
||||
uint32_t subtype = GetTypeInstruction(image->sampled_type());
|
||||
if (subtype == 0) {
|
||||
return 0;
|
||||
}
|
||||
typeInst = MakeUnique<Instruction>(
|
||||
context(), SpvOpTypeImage, 0, id,
|
||||
std::initializer_list<Operand>{
|
||||
@ -289,6 +302,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
case Type::kSampledImage: {
|
||||
uint32_t subtype =
|
||||
GetTypeInstruction(type->AsSampledImage()->image_type());
|
||||
if (subtype == 0) {
|
||||
return 0;
|
||||
}
|
||||
typeInst = MakeUnique<Instruction>(
|
||||
context(), SpvOpTypeSampledImage, 0, id,
|
||||
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}});
|
||||
@ -296,6 +312,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
}
|
||||
case Type::kArray: {
|
||||
uint32_t subtype = GetTypeInstruction(type->AsArray()->element_type());
|
||||
if (subtype == 0) {
|
||||
return 0;
|
||||
}
|
||||
typeInst = MakeUnique<Instruction>(
|
||||
context(), SpvOpTypeArray, 0, id,
|
||||
std::initializer_list<Operand>{
|
||||
@ -306,6 +325,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
case Type::kRuntimeArray: {
|
||||
uint32_t subtype =
|
||||
GetTypeInstruction(type->AsRuntimeArray()->element_type());
|
||||
if (subtype == 0) {
|
||||
return 0;
|
||||
}
|
||||
typeInst = MakeUnique<Instruction>(
|
||||
context(), SpvOpTypeRuntimeArray, 0, id,
|
||||
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}});
|
||||
@ -315,7 +337,11 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
std::vector<Operand> ops;
|
||||
const Struct* structTy = type->AsStruct();
|
||||
for (auto ty : structTy->element_types()) {
|
||||
ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {GetTypeInstruction(ty)}));
|
||||
uint32_t member_type_id = GetTypeInstruction(ty);
|
||||
if (member_type_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {member_type_id}));
|
||||
}
|
||||
typeInst =
|
||||
MakeUnique<Instruction>(context(), SpvOpTypeStruct, 0, id, ops);
|
||||
@ -337,6 +363,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
case Type::kPointer: {
|
||||
const Pointer* pointer = type->AsPointer();
|
||||
uint32_t subtype = GetTypeInstruction(pointer->pointee_type());
|
||||
if (subtype == 0) {
|
||||
return 0;
|
||||
}
|
||||
typeInst = MakeUnique<Instruction>(
|
||||
context(), SpvOpTypePointer, 0, id,
|
||||
std::initializer_list<Operand>{
|
||||
@ -348,10 +377,17 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
case Type::kFunction: {
|
||||
std::vector<Operand> ops;
|
||||
const Function* function = type->AsFunction();
|
||||
ops.push_back(Operand(SPV_OPERAND_TYPE_ID,
|
||||
{GetTypeInstruction(function->return_type())}));
|
||||
uint32_t return_type_id = GetTypeInstruction(function->return_type());
|
||||
if (return_type_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {return_type_id}));
|
||||
for (auto ty : function->param_types()) {
|
||||
ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {GetTypeInstruction(ty)}));
|
||||
uint32_t paramater_type_id = GetTypeInstruction(ty);
|
||||
if (paramater_type_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {paramater_type_id}));
|
||||
}
|
||||
typeInst =
|
||||
MakeUnique<Instruction>(context(), SpvOpTypeFunction, 0, id, ops);
|
||||
@ -594,6 +630,9 @@ void TypeManager::RegisterType(uint32_t id, const Type& type) {
|
||||
|
||||
Type* TypeManager::GetRegisteredType(const Type* type) {
|
||||
uint32_t id = GetTypeInstruction(type);
|
||||
if (id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return GetType(id);
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,8 @@ class TypeManager {
|
||||
std::pair<Type*, std::unique_ptr<Pointer>> GetTypeAndPointerType(
|
||||
uint32_t id, SpvStorageClass sc) const;
|
||||
|
||||
// Returns an id for a declaration representing |type|.
|
||||
// Returns an id for a declaration representing |type|. Returns 0 if the type
|
||||
// does not exists, and could not be generated.
|
||||
//
|
||||
// If |type| is registered, then the registered id is returned. Otherwise,
|
||||
// this function recursively adds type and annotation instructions as
|
||||
@ -109,7 +110,8 @@ class TypeManager {
|
||||
uint32_t GetTypeInstruction(const Type* type);
|
||||
|
||||
// Find pointer to type and storage in module, return its resultId. If it is
|
||||
// not found, a new type is created, and its id is returned.
|
||||
// not found, a new type is created, and its id is returned. Returns 0 if the
|
||||
// type could not be created.
|
||||
uint32_t FindPointerToType(uint32_t type_id, SpvStorageClass storage_class);
|
||||
|
||||
// Registers |id| to |type|.
|
||||
@ -118,6 +120,7 @@ class TypeManager {
|
||||
// unchanged.
|
||||
void RegisterType(uint32_t id, const Type& type);
|
||||
|
||||
// Return the registered type object that is the same as |type|.
|
||||
Type* GetRegisteredType(const Type* type);
|
||||
|
||||
// Removes knowledge of |id| from the manager.
|
||||
|
4
3rdparty/spirv-tools/source/opt/types.cpp
vendored
4
3rdparty/spirv-tools/source/opt/types.cpp
vendored
@ -571,10 +571,10 @@ void Pointer::GetExtraHashWords(std::vector<uint32_t>* words,
|
||||
|
||||
void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; }
|
||||
|
||||
Function::Function(Type* ret_type, const std::vector<const Type*>& params)
|
||||
Function::Function(const Type* ret_type, const std::vector<const Type*>& params)
|
||||
: Type(kFunction), return_type_(ret_type), param_types_(params) {}
|
||||
|
||||
Function::Function(Type* ret_type, std::vector<const Type*>& params)
|
||||
Function::Function(const Type* ret_type, std::vector<const Type*>& params)
|
||||
: Type(kFunction), return_type_(ret_type), param_types_(params) {}
|
||||
|
||||
bool Function::IsSameImpl(const Type* that, IsSameCache* seen) const {
|
||||
|
4
3rdparty/spirv-tools/source/opt/types.h
vendored
4
3rdparty/spirv-tools/source/opt/types.h
vendored
@ -520,8 +520,8 @@ class Pointer : public Type {
|
||||
|
||||
class Function : public Type {
|
||||
public:
|
||||
Function(Type* ret_type, const std::vector<const Type*>& params);
|
||||
Function(Type* ret_type, std::vector<const Type*>& params);
|
||||
Function(const Type* ret_type, const std::vector<const Type*>& params);
|
||||
Function(const Type* ret_type, std::vector<const Type*>& params);
|
||||
Function(const Function&) = default;
|
||||
|
||||
std::string str() const override;
|
||||
|
146
3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
vendored
Normal file
146
3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "source/opt/wrap_opkill.h"
|
||||
|
||||
#include "ir_builder.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status WrapOpKill::Process() {
|
||||
bool modified = false;
|
||||
|
||||
for (auto& func : *get_module()) {
|
||||
bool successful = func.WhileEachInst([this, &modified](Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpKill) {
|
||||
modified = true;
|
||||
if (!ReplaceWithFunctionCall(inst)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!successful) {
|
||||
return Status::Failure;
|
||||
}
|
||||
}
|
||||
|
||||
if (opkill_function_ != nullptr) {
|
||||
assert(modified &&
|
||||
"The function should only be generated if something was modified.");
|
||||
context()->AddFunction(std::move(opkill_function_));
|
||||
}
|
||||
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
|
||||
}
|
||||
|
||||
bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
|
||||
assert(inst->opcode() == SpvOpKill &&
|
||||
"|inst| must be an OpKill instruction.");
|
||||
InstructionBuilder ir_builder(
|
||||
context(), inst,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
uint32_t func_id = GetOpKillFuncId();
|
||||
if (func_id == 0) {
|
||||
return false;
|
||||
}
|
||||
if (ir_builder.AddFunctionCall(GetVoidTypeId(), func_id, {}) == nullptr) {
|
||||
return false;
|
||||
}
|
||||
ir_builder.AddUnreachable();
|
||||
context()->KillInst(inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t WrapOpKill::GetVoidTypeId() {
|
||||
if (void_type_id_ != 0) {
|
||||
return void_type_id_;
|
||||
}
|
||||
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::Void void_type;
|
||||
void_type_id_ = type_mgr->GetTypeInstruction(&void_type);
|
||||
return void_type_id_;
|
||||
}
|
||||
|
||||
uint32_t WrapOpKill::GetVoidFunctionTypeId() {
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::Void void_type;
|
||||
const analysis::Type* registered_void_type =
|
||||
type_mgr->GetRegisteredType(&void_type);
|
||||
|
||||
analysis::Function func_type(registered_void_type, {});
|
||||
return type_mgr->GetTypeInstruction(&func_type);
|
||||
}
|
||||
|
||||
uint32_t WrapOpKill::GetOpKillFuncId() {
|
||||
if (opkill_function_ != nullptr) {
|
||||
return opkill_function_->result_id();
|
||||
}
|
||||
|
||||
uint32_t opkill_func_id = TakeNextId();
|
||||
if (opkill_func_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Generate the function start instruction
|
||||
std::unique_ptr<Instruction> func_start(new Instruction(
|
||||
context(), SpvOpFunction, GetVoidTypeId(), opkill_func_id, {}));
|
||||
func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}});
|
||||
func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}});
|
||||
opkill_function_.reset(new Function(std::move(func_start)));
|
||||
|
||||
// Generate the function end instruction
|
||||
std::unique_ptr<Instruction> func_end(
|
||||
new Instruction(context(), SpvOpFunctionEnd, 0, 0, {}));
|
||||
opkill_function_->SetFunctionEnd(std::move(func_end));
|
||||
|
||||
// Create the one basic block for the function.
|
||||
uint32_t lab_id = TakeNextId();
|
||||
if (lab_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
std::unique_ptr<Instruction> label_inst(
|
||||
new Instruction(context(), SpvOpLabel, 0, lab_id, {}));
|
||||
std::unique_ptr<BasicBlock> bb(new BasicBlock(std::move(label_inst)));
|
||||
|
||||
// Add the OpKill to the basic block
|
||||
std::unique_ptr<Instruction> kill_inst(
|
||||
new Instruction(context(), SpvOpKill, 0, 0, {}));
|
||||
bb->AddInstruction(std::move(kill_inst));
|
||||
|
||||
// Add the bb to the function
|
||||
opkill_function_->AddBasicBlock(std::move(bb));
|
||||
|
||||
// Add the function to the module.
|
||||
if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) {
|
||||
opkill_function_->ForEachInst(
|
||||
[this](Instruction* inst) { context()->AnalyzeDefUse(inst); });
|
||||
}
|
||||
|
||||
if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
|
||||
for (BasicBlock& basic_block : *opkill_function_) {
|
||||
context()->set_instr_block(basic_block.GetLabelInst(), &basic_block);
|
||||
for (Instruction& inst : basic_block) {
|
||||
context()->set_instr_block(&inst, &basic_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return opkill_function_->result_id();
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
72
3rdparty/spirv-tools/source/opt/wrap_opkill.h
vendored
Normal file
72
3rdparty/spirv-tools/source/opt/wrap_opkill.h
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2019 Google LLC
|
||||
//
|
||||
// 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 SOURCE_OPT_WRAP_OPKILL_H_
|
||||
#define SOURCE_OPT_WRAP_OPKILL_H_
|
||||
|
||||
#include "source/opt/pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// Documented in optimizer.hpp
|
||||
class WrapOpKill : public Pass {
|
||||
public:
|
||||
WrapOpKill() : void_type_id_(0) {}
|
||||
|
||||
const char* name() const override { return "wrap-opkill"; }
|
||||
|
||||
Status Process() override;
|
||||
|
||||
IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return IRContext::kAnalysisDefUse |
|
||||
IRContext::kAnalysisInstrToBlockMapping |
|
||||
IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
|
||||
IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId |
|
||||
IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants |
|
||||
IRContext::kAnalysisTypes;
|
||||
}
|
||||
|
||||
private:
|
||||
// Replaces the OpKill instruction |inst| with a function call to a function
|
||||
// that contains a single instruction, which is OpKill. An OpUnreachable
|
||||
// instruction will be placed after the function call. Return true if
|
||||
// successful.
|
||||
bool ReplaceWithFunctionCall(Instruction* inst);
|
||||
|
||||
// Returns the id of the void type.
|
||||
uint32_t GetVoidTypeId();
|
||||
|
||||
// Returns the id of the function type for a void function with no parameters.
|
||||
uint32_t GetVoidFunctionTypeId();
|
||||
|
||||
// Return the id of a function that has return type void, has no parameters,
|
||||
// and contains a single instruction, which is an OpKill. Returns 0 if the
|
||||
// function could not be generated.
|
||||
uint32_t GetOpKillFuncId();
|
||||
|
||||
// The id of the void type. If its value is 0, then the void type has not
|
||||
// been found or created yet.
|
||||
uint32_t void_type_id_;
|
||||
|
||||
// The function that is a single instruction, which is an OpKill. The
|
||||
// function has a void return type and takes no parameters. If the function is
|
||||
// |nullptr|, then the function has not been generated.
|
||||
std::unique_ptr<Function> opkill_function_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_OPT_WRAP_OPKILL_H_
|
@ -30,6 +30,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
remove_function_reduction_opportunity.h
|
||||
remove_function_reduction_opportunity_finder.h
|
||||
remove_opname_instruction_reduction_opportunity_finder.h
|
||||
remove_relaxed_precision_decoration_opportunity_finder.h
|
||||
remove_selection_reduction_opportunity.h
|
||||
remove_selection_reduction_opportunity_finder.h
|
||||
remove_unreferenced_instruction_reduction_opportunity_finder.h
|
||||
@ -56,6 +57,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
remove_function_reduction_opportunity.cpp
|
||||
remove_function_reduction_opportunity_finder.cpp
|
||||
remove_instruction_reduction_opportunity.cpp
|
||||
remove_relaxed_precision_decoration_opportunity_finder.cpp
|
||||
remove_selection_reduction_opportunity.cpp
|
||||
remove_selection_reduction_opportunity_finder.cpp
|
||||
remove_unreferenced_instruction_reduction_opportunity_finder.cpp
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "source/reduce/remove_block_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_function_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
|
||||
#include "source/reduce/remove_selection_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
|
||||
@ -175,6 +176,8 @@ Reducer::ReductionResultStatus Reducer::Run(
|
||||
void Reducer::AddDefaultReductionPasses() {
|
||||
AddReductionPass(spvtools::MakeUnique<
|
||||
RemoveOpNameInstructionReductionOpportunityFinder>());
|
||||
AddReductionPass(spvtools::MakeUnique<
|
||||
RemoveRelaxedPrecisionDecorationOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
|
49
3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp
vendored
Normal file
49
3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
|
||||
|
||||
#include "source/reduce/remove_instruction_reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
RemoveRelaxedPrecisionDecorationOpportunityFinder::GetAvailableOpportunities(
|
||||
opt::IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
// Consider all annotation instructions
|
||||
for (auto& inst : context->module()->annotations()) {
|
||||
// We are interested in removing instructions of the form:
|
||||
// SpvOpDecorate %id RelaxedPrecision
|
||||
// and
|
||||
// SpvOpMemberDecorate %id member RelaxedPrecision
|
||||
if ((inst.opcode() == SpvOpDecorate &&
|
||||
inst.GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) ||
|
||||
(inst.opcode() == SpvOpMemberDecorate &&
|
||||
inst.GetSingleWordInOperand(2) == SpvDecorationRelaxedPrecision)) {
|
||||
result.push_back(
|
||||
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string RemoveRelaxedPrecisionDecorationOpportunityFinder::GetName() const {
|
||||
return "RemoveRelaxedPrecisionDecorationOpportunityFinder";
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
36
3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h
vendored
Normal file
36
3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2019 Google LLC
|
||||
//
|
||||
// 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 SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
|
||||
#define SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
|
||||
|
||||
#include "source/reduce/reduction_opportunity_finder.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// A finder for opportunities to remove relaxed precision decorations.
|
||||
class RemoveRelaxedPrecisionDecorationOpportunityFinder
|
||||
: public ReductionOpportunityFinder {
|
||||
public:
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
|
||||
opt::IRContext* context) const override;
|
||||
|
||||
std::string GetName() const override;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
|
@ -21,11 +21,9 @@
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using opt::IRContext;
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder::
|
||||
GetAvailableOpportunities(IRContext* context) const {
|
||||
GetAvailableOpportunities(opt::IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
for (auto& function : *context->module()) {
|
||||
|
@ -116,15 +116,13 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _,
|
||||
inst->GetOperandAs<uint32_t>(constituent_index);
|
||||
const auto constituent = _.FindDef(constituent_id);
|
||||
if (!constituent ||
|
||||
!(SpvOpConstantComposite == constituent->opcode() ||
|
||||
SpvOpSpecConstantComposite == constituent->opcode() ||
|
||||
SpvOpUndef == constituent->opcode())) {
|
||||
!spvOpcodeIsConstantOrUndef(constituent->opcode())) {
|
||||
// The message says "... or undef" because the spec does not say
|
||||
// undef is a constant.
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< opcode_name << " Constituent <id> '"
|
||||
<< _.getIdName(constituent_id)
|
||||
<< "' is not a constant composite or undef.";
|
||||
<< "' is not a constant or undef.";
|
||||
}
|
||||
const auto vector = _.FindDef(constituent->type_id());
|
||||
if (!vector) {
|
||||
|
@ -923,7 +923,58 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
case OpenCLLIB::Fract:
|
||||
case OpenCLLIB::Modf:
|
||||
case OpenCLLIB::Sincos:
|
||||
case OpenCLLIB::Sincos: {
|
||||
if (!_.IsFloatScalarOrVectorType(result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected Result Type to be a float scalar or vector type";
|
||||
}
|
||||
|
||||
const uint32_t num_components = _.GetDimension(result_type);
|
||||
if (num_components > 4 && num_components != 8 && num_components != 16) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected Result Type to be a scalar or a vector with 2, "
|
||||
"3, 4, 8 or 16 components";
|
||||
}
|
||||
|
||||
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
|
||||
if (result_type != x_type) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected type of operand X to be equal to Result Type";
|
||||
}
|
||||
|
||||
const uint32_t p_type = _.GetOperandTypeId(inst, 5);
|
||||
uint32_t p_storage_class = 0;
|
||||
uint32_t p_data_type = 0;
|
||||
if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected the last operand to be a pointer";
|
||||
}
|
||||
|
||||
if (p_storage_class != SpvStorageClassGeneric &&
|
||||
p_storage_class != SpvStorageClassCrossWorkgroup &&
|
||||
p_storage_class != SpvStorageClassWorkgroup &&
|
||||
p_storage_class != SpvStorageClassFunction) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected storage class of the pointer to be Generic, "
|
||||
"CrossWorkgroup, Workgroup or Function";
|
||||
}
|
||||
|
||||
if (result_type != p_data_type) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected data type of the pointer to be equal to Result "
|
||||
"Type";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OpenCLLIB::Frexp:
|
||||
case OpenCLLIB::Lgamma_r:
|
||||
case OpenCLLIB::Remquo: {
|
||||
if (!_.IsFloatScalarOrVectorType(result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
@ -975,57 +1026,6 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
"CrossWorkgroup, Workgroup or Function";
|
||||
}
|
||||
|
||||
if (result_type != p_data_type) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected data type of the pointer to be equal to Result "
|
||||
"Type";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OpenCLLIB::Frexp:
|
||||
case OpenCLLIB::Lgamma_r: {
|
||||
if (!_.IsFloatScalarOrVectorType(result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected Result Type to be a float scalar or vector type";
|
||||
}
|
||||
|
||||
const uint32_t num_components = _.GetDimension(result_type);
|
||||
if (num_components > 4 && num_components != 8 && num_components != 16) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected Result Type to be a scalar or a vector with 2, "
|
||||
"3, 4, 8 or 16 components";
|
||||
}
|
||||
|
||||
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
|
||||
if (result_type != x_type) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected type of operand X to be equal to Result Type";
|
||||
}
|
||||
|
||||
const uint32_t p_type = _.GetOperandTypeId(inst, 5);
|
||||
uint32_t p_storage_class = 0;
|
||||
uint32_t p_data_type = 0;
|
||||
if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected the last operand to be a pointer";
|
||||
}
|
||||
|
||||
if (p_storage_class != SpvStorageClassGeneric &&
|
||||
p_storage_class != SpvStorageClassCrossWorkgroup &&
|
||||
p_storage_class != SpvStorageClassWorkgroup &&
|
||||
p_storage_class != SpvStorageClassFunction) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected storage class of the pointer to be Generic, "
|
||||
"CrossWorkgroup, Workgroup or Function";
|
||||
}
|
||||
|
||||
if (!_.IsIntScalarOrVectorType(p_data_type) ||
|
||||
_.GetBitWidth(p_data_type) != 32) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
|
@ -84,44 +84,46 @@ using TypeMatch = spvtest::LinkerTest;
|
||||
MatchF(T##Of##A##Of##B, \
|
||||
MatchPart1(B, b) MatchPart2(A, a, b) MatchPart2(T, type, a))
|
||||
|
||||
// clang-format off
|
||||
// Basic types
|
||||
Match1(Int);
|
||||
Match1(Float);
|
||||
Match1(Opaque);
|
||||
Match1(Sampler);
|
||||
Match1(Event);
|
||||
Match1(DeviceEvent);
|
||||
Match1(ReserveId);
|
||||
Match1(Queue);
|
||||
Match1(Pipe);
|
||||
Match1(PipeStorage);
|
||||
Match1(NamedBarrier);
|
||||
Match1(Int)
|
||||
Match1(Float)
|
||||
Match1(Opaque)
|
||||
Match1(Sampler)
|
||||
Match1(Event)
|
||||
Match1(DeviceEvent)
|
||||
Match1(ReserveId)
|
||||
Match1(Queue)
|
||||
Match1(Pipe)
|
||||
Match1(PipeStorage)
|
||||
Match1(NamedBarrier)
|
||||
|
||||
// Simpler (restricted) compound types
|
||||
Match2(Vector, Float);
|
||||
Match3(Matrix, Vector, Float);
|
||||
Match2(Image, Float);
|
||||
Match2(Vector, Float)
|
||||
Match3(Matrix, Vector, Float)
|
||||
Match2(Image, Float)
|
||||
|
||||
// Unrestricted compound types
|
||||
#define MatchCompounds1(A) \
|
||||
Match2(RuntimeArray, A); \
|
||||
Match2(Struct, A); \
|
||||
Match2(Pointer, A); \
|
||||
Match2(Function, A); \
|
||||
Match2(Array, A);
|
||||
Match2(RuntimeArray, A) \
|
||||
Match2(Struct, A) \
|
||||
Match2(Pointer, A) \
|
||||
Match2(Function, A) \
|
||||
Match2(Array, A)
|
||||
#define MatchCompounds2(A, B) \
|
||||
Match3(RuntimeArray, A, B); \
|
||||
Match3(Struct, A, B); \
|
||||
Match3(Pointer, A, B); \
|
||||
Match3(Function, A, B); \
|
||||
Match3(Array, A, B);
|
||||
Match3(RuntimeArray, A, B) \
|
||||
Match3(Struct, A, B) \
|
||||
Match3(Pointer, A, B) \
|
||||
Match3(Function, A, B) \
|
||||
Match3(Array, A, B)
|
||||
|
||||
MatchCompounds1(Float);
|
||||
MatchCompounds2(Array, Float);
|
||||
MatchCompounds2(RuntimeArray, Float);
|
||||
MatchCompounds2(Struct, Float);
|
||||
MatchCompounds2(Pointer, Float);
|
||||
MatchCompounds2(Function, Float);
|
||||
MatchCompounds1(Float)
|
||||
MatchCompounds2(Array, Float)
|
||||
MatchCompounds2(RuntimeArray, Float)
|
||||
MatchCompounds2(Struct, Float)
|
||||
MatchCompounds2(Pointer, Float)
|
||||
MatchCompounds2(Function, Float)
|
||||
// clang-format on
|
||||
|
||||
// ForwardPointer tests, which don't fit into the previous mold
|
||||
#define MatchFpF(N, CODE) \
|
||||
@ -134,11 +136,13 @@ MatchCompounds2(Function, Float);
|
||||
#define MatchFp2(T, A) \
|
||||
MatchFpF(ForwardPointerOf##T, MatchPart1(A, a) MatchPart2(T, realtype, a))
|
||||
|
||||
MatchFp1(Float);
|
||||
MatchFp2(Array, Float);
|
||||
MatchFp2(RuntimeArray, Float);
|
||||
MatchFp2(Struct, Float);
|
||||
MatchFp2(Function, Float);
|
||||
// clang-format off
|
||||
MatchFp1(Float)
|
||||
MatchFp2(Array, Float)
|
||||
MatchFp2(RuntimeArray, Float)
|
||||
MatchFp2(Struct, Float)
|
||||
MatchFp2(Function, Float)
|
||||
// clang-format on
|
||||
|
||||
} // namespace
|
||||
} // namespace spvtools
|
||||
|
2
3rdparty/spirv-tools/test/opt/CMakeLists.txt
vendored
2
3rdparty/spirv-tools/test/opt/CMakeLists.txt
vendored
@ -52,6 +52,7 @@ add_spvtools_unittest(TARGET opt
|
||||
inline_test.cpp
|
||||
insert_extract_elim_test.cpp
|
||||
inst_bindless_check_test.cpp
|
||||
inst_buff_addr_check_test.cpp
|
||||
instruction_list_test.cpp
|
||||
instruction_test.cpp
|
||||
ir_builder.cpp
|
||||
@ -97,6 +98,7 @@ add_spvtools_unittest(TARGET opt
|
||||
value_table_test.cpp
|
||||
vector_dce_test.cpp
|
||||
workaround1209_test.cpp
|
||||
wrap_opkill_test.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
PCH_FILE pch_test_opt
|
||||
)
|
||||
|
61
3rdparty/spirv-tools/test/opt/desc_sroa_test.cpp
vendored
61
3rdparty/spirv-tools/test/opt/desc_sroa_test.cpp
vendored
@ -204,6 +204,67 @@ TEST_F(DescriptorScalarReplacementTest, ExpandSSBO) {
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, NameNewVariables) {
|
||||
// Checks that if the original variable has a name, then the new variables
|
||||
// will have a name derived from that name.
|
||||
const std::string text = R"(
|
||||
; CHECK: OpName [[var1:%\w+]] "SSBO[0]"
|
||||
; CHECK: OpName [[var2:%\w+]] "SSBO[1]"
|
||||
; CHECK: OpDecorate [[var1]] DescriptorSet 0
|
||||
; CHECK: OpDecorate [[var1]] Binding 0
|
||||
; CHECK: OpDecorate [[var2]] DescriptorSet 0
|
||||
; CHECK: OpDecorate [[var2]] Binding 1
|
||||
; CHECK: OpTypeStruct
|
||||
; CHECK: [[struct_type:%\w+]] = OpTypeStruct
|
||||
; CHECK: [[ptr_type:%\w+]] = OpTypePointer Uniform [[struct_type]]
|
||||
; CHECK: [[var1]] = OpVariable [[ptr_type]] Uniform
|
||||
; CHECK: [[var2]] = OpVariable [[ptr_type]] Uniform
|
||||
; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var1]] %uint_0 %uint_0 %uint_0
|
||||
; CHECK: OpLoad %v4float [[ac1]]
|
||||
; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var2]] %uint_0 %uint_0 %uint_0
|
||||
; CHECK: OpLoad %v4float [[ac2]]
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource HLSL 600
|
||||
OpName %buffers "SSBO"
|
||||
OpDecorate %buffers DescriptorSet 0
|
||||
OpDecorate %buffers Binding 0
|
||||
OpMemberDecorate %S 0 Offset 0
|
||||
OpDecorate %_runtimearr_S ArrayStride 16
|
||||
OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0
|
||||
OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable
|
||||
OpDecorate %type_StructuredBuffer_S BufferBlock
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%uint_2 = OpConstant %uint 2
|
||||
%float = OpTypeFloat 32
|
||||
%v4float = OpTypeVector %float 4
|
||||
%S = OpTypeStruct %v4float
|
||||
%_runtimearr_S = OpTypeRuntimeArray %S
|
||||
%type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S
|
||||
%_arr_type_StructuredBuffer_S_uint_2 = OpTypeArray %type_StructuredBuffer_S %uint_2
|
||||
%_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 = OpTypePointer Uniform %_arr_type_StructuredBuffer_S_uint_2
|
||||
%_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S
|
||||
%void = OpTypeVoid
|
||||
%19 = OpTypeFunction %void
|
||||
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
|
||||
%buffers = OpVariable %_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 Uniform
|
||||
%main = OpFunction %void None %19
|
||||
%21 = OpLabel
|
||||
%22 = OpAccessChain %_ptr_Uniform_v4float %buffers %uint_0 %uint_0 %uint_0 %uint_0
|
||||
%23 = OpLoad %v4float %22
|
||||
%24 = OpAccessChain %_ptr_Uniform_type_StructuredBuffer_S %buffers %uint_1
|
||||
%25 = OpAccessChain %_ptr_Uniform_v4float %24 %uint_0 %uint_0 %uint_0
|
||||
%26 = OpLoad %v4float %25
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
}
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
620
3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp
vendored
Normal file
620
3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp
vendored
Normal file
@ -0,0 +1,620 @@
|
||||
// Copyright (c) 2019 Valve Corporation
|
||||
// Copyright (c) 2019 LunarG 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.
|
||||
|
||||
// Bindless Check Instrumentation Tests.
|
||||
// Tests ending with V2 use version 2 record format.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "test/opt/assembly_builder.h"
|
||||
#include "test/opt/pass_fixture.h"
|
||||
#include "test/opt/pass_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
using InstBuffAddrTest = PassTest<::testing::Test>;
|
||||
|
||||
TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) {
|
||||
// #version 450
|
||||
// #extension GL_EXT_buffer_reference : enable
|
||||
//
|
||||
// layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;
|
||||
//
|
||||
// layout(set = 0, binding = 0) uniform ufoo {
|
||||
// bufStruct data;
|
||||
// uint offset;
|
||||
// } u_info;
|
||||
//
|
||||
// layout(buffer_reference, std140) buffer bufStruct {
|
||||
// layout(offset = 0) int a[2];
|
||||
// layout(offset = 32) int b;
|
||||
// };
|
||||
//
|
||||
// void main() {
|
||||
// u_info.data.b = 0xca7;
|
||||
// }
|
||||
|
||||
const std::string defs_before =
|
||||
R"(OpCapability Shader
|
||||
OpCapability PhysicalStorageBufferAddressesEXT
|
||||
OpExtension "SPV_EXT_physical_storage_buffer"
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpSource GLSL 450
|
||||
OpSourceExtension "GL_EXT_buffer_reference"
|
||||
OpName %main "main"
|
||||
OpName %ufoo "ufoo"
|
||||
OpMemberName %ufoo 0 "data"
|
||||
OpMemberName %ufoo 1 "offset"
|
||||
OpName %bufStruct "bufStruct"
|
||||
OpMemberName %bufStruct 0 "a"
|
||||
OpMemberName %bufStruct 1 "b"
|
||||
OpName %u_info "u_info"
|
||||
OpMemberDecorate %ufoo 0 Offset 0
|
||||
OpMemberDecorate %ufoo 1 Offset 8
|
||||
OpDecorate %ufoo Block
|
||||
OpDecorate %_arr_int_uint_2 ArrayStride 16
|
||||
OpMemberDecorate %bufStruct 0 Offset 0
|
||||
OpMemberDecorate %bufStruct 1 Offset 32
|
||||
OpDecorate %bufStruct Block
|
||||
OpDecorate %u_info DescriptorSet 0
|
||||
OpDecorate %u_info Binding 0
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT
|
||||
%uint = OpTypeInt 32 0
|
||||
%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint
|
||||
%int = OpTypeInt 32 1
|
||||
%uint_2 = OpConstant %uint 2
|
||||
%_arr_int_uint_2 = OpTypeArray %int %uint_2
|
||||
%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
|
||||
%_ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct
|
||||
%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
|
||||
%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
|
||||
%int_0 = OpConstant %int 0
|
||||
%_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_3239 = OpConstant %int 3239
|
||||
%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
|
||||
)";
|
||||
|
||||
const std::string defs_after =
|
||||
R"(OpCapability Shader
|
||||
OpCapability PhysicalStorageBufferAddressesEXT
|
||||
OpCapability Int64
|
||||
OpExtension "SPV_EXT_physical_storage_buffer"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpSource GLSL 450
|
||||
OpSourceExtension "GL_EXT_buffer_reference"
|
||||
OpName %main "main"
|
||||
OpName %ufoo "ufoo"
|
||||
OpMemberName %ufoo 0 "data"
|
||||
OpMemberName %ufoo 1 "offset"
|
||||
OpName %bufStruct "bufStruct"
|
||||
OpMemberName %bufStruct 0 "a"
|
||||
OpMemberName %bufStruct 1 "b"
|
||||
OpName %u_info "u_info"
|
||||
OpMemberDecorate %ufoo 0 Offset 0
|
||||
OpMemberDecorate %ufoo 1 Offset 8
|
||||
OpDecorate %ufoo Block
|
||||
OpDecorate %_arr_int_uint_2 ArrayStride 16
|
||||
OpMemberDecorate %bufStruct 0 Offset 0
|
||||
OpMemberDecorate %bufStruct 1 Offset 32
|
||||
OpDecorate %bufStruct Block
|
||||
OpDecorate %u_info DescriptorSet 0
|
||||
OpDecorate %u_info Binding 0
|
||||
OpDecorate %_runtimearr_ulong ArrayStride 8
|
||||
OpDecorate %_struct_39 Block
|
||||
OpMemberDecorate %_struct_39 0 Offset 0
|
||||
OpDecorate %41 DescriptorSet 7
|
||||
OpDecorate %41 Binding 2
|
||||
OpDecorate %_runtimearr_uint ArrayStride 4
|
||||
OpDecorate %_struct_77 Block
|
||||
OpMemberDecorate %_struct_77 0 Offset 0
|
||||
OpMemberDecorate %_struct_77 1 Offset 4
|
||||
OpDecorate %79 DescriptorSet 7
|
||||
OpDecorate %79 Binding 0
|
||||
OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
|
||||
%void = OpTypeVoid
|
||||
%8 = OpTypeFunction %void
|
||||
OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT
|
||||
%uint = OpTypeInt 32 0
|
||||
%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint
|
||||
%int = OpTypeInt 32 1
|
||||
%uint_2 = OpConstant %uint 2
|
||||
%_arr_int_uint_2 = OpTypeArray %int %uint_2
|
||||
%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
|
||||
%_ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct
|
||||
%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
|
||||
%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
|
||||
%int_0 = OpConstant %int 0
|
||||
%_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_3239 = OpConstant %int 3239
|
||||
%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
|
||||
%ulong = OpTypeInt 64 0
|
||||
%uint_4 = OpConstant %uint 4
|
||||
%bool = OpTypeBool
|
||||
%28 = OpTypeFunction %bool %ulong %uint
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%_runtimearr_ulong = OpTypeRuntimeArray %ulong
|
||||
%_struct_39 = OpTypeStruct %_runtimearr_ulong
|
||||
%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
|
||||
%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
|
||||
%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%uint_32 = OpConstant %uint 32
|
||||
%70 = OpTypeFunction %void %uint %uint %uint %uint
|
||||
%_runtimearr_uint = OpTypeRuntimeArray %uint
|
||||
%_struct_77 = OpTypeStruct %uint %_runtimearr_uint
|
||||
%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77
|
||||
%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer
|
||||
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
|
||||
%uint_10 = OpConstant %uint 10
|
||||
%uint_23 = OpConstant %uint 23
|
||||
%uint_5 = OpConstant %uint 5
|
||||
%uint_3 = OpConstant %uint 3
|
||||
%v3uint = OpTypeVector %uint 3
|
||||
%_ptr_Input_v3uint = OpTypePointer Input %v3uint
|
||||
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
||||
%uint_6 = OpConstant %uint 6
|
||||
%uint_7 = OpConstant %uint 7
|
||||
%uint_8 = OpConstant %uint 8
|
||||
%uint_9 = OpConstant %uint 9
|
||||
%uint_48 = OpConstant %uint 48
|
||||
)";
|
||||
|
||||
const std::string func_before =
|
||||
R"(%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0
|
||||
%18 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %17
|
||||
%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %18 %int_1
|
||||
OpStore %22 %int_3239 Aligned 16
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string func_after =
|
||||
R"(%main = OpFunction %void None %8
|
||||
%19 = OpLabel
|
||||
%20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0
|
||||
%21 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %20
|
||||
%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %21 %int_1
|
||||
%24 = OpConvertPtrToU %ulong %22
|
||||
%61 = OpFunctionCall %bool %26 %24 %uint_4
|
||||
OpSelectionMerge %62 None
|
||||
OpBranchConditional %61 %63 %64
|
||||
%63 = OpLabel
|
||||
OpStore %22 %int_3239 Aligned 16
|
||||
OpBranch %62
|
||||
%64 = OpLabel
|
||||
%65 = OpUConvert %uint %24
|
||||
%67 = OpShiftRightLogical %ulong %24 %uint_32
|
||||
%68 = OpUConvert %uint %67
|
||||
%124 = OpFunctionCall %void %69 %uint_48 %uint_2 %65 %68
|
||||
OpBranch %62
|
||||
%62 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string new_funcs =
|
||||
R"(%26 = OpFunction %bool None %28
|
||||
%29 = OpFunctionParameter %ulong
|
||||
%30 = OpFunctionParameter %uint
|
||||
%31 = OpLabel
|
||||
OpBranch %32
|
||||
%32 = OpLabel
|
||||
%34 = OpPhi %uint %uint_1 %31 %35 %33
|
||||
OpLoopMerge %37 %33 None
|
||||
OpBranch %33
|
||||
%33 = OpLabel
|
||||
%35 = OpIAdd %uint %34 %uint_1
|
||||
%44 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %35
|
||||
%45 = OpLoad %ulong %44
|
||||
%46 = OpUGreaterThan %bool %45 %29
|
||||
OpBranchConditional %46 %37 %32
|
||||
%37 = OpLabel
|
||||
%47 = OpISub %uint %35 %uint_1
|
||||
%48 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %47
|
||||
%49 = OpLoad %ulong %48
|
||||
%50 = OpISub %ulong %29 %49
|
||||
%51 = OpUConvert %ulong %30
|
||||
%52 = OpIAdd %ulong %50 %51
|
||||
%53 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %uint_0
|
||||
%54 = OpLoad %ulong %53
|
||||
%55 = OpUConvert %uint %54
|
||||
%56 = OpISub %uint %47 %uint_1
|
||||
%57 = OpIAdd %uint %56 %55
|
||||
%58 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %57
|
||||
%59 = OpLoad %ulong %58
|
||||
%60 = OpULessThanEqual %bool %52 %59
|
||||
OpReturnValue %60
|
||||
OpFunctionEnd
|
||||
%69 = OpFunction %void None %70
|
||||
%71 = OpFunctionParameter %uint
|
||||
%72 = OpFunctionParameter %uint
|
||||
%73 = OpFunctionParameter %uint
|
||||
%74 = OpFunctionParameter %uint
|
||||
%75 = OpLabel
|
||||
%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0
|
||||
%83 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10
|
||||
%84 = OpIAdd %uint %83 %uint_10
|
||||
%85 = OpArrayLength %uint %79 1
|
||||
%86 = OpULessThanEqual %bool %84 %85
|
||||
OpSelectionMerge %87 None
|
||||
OpBranchConditional %86 %88 %87
|
||||
%88 = OpLabel
|
||||
%89 = OpIAdd %uint %83 %uint_0
|
||||
%90 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %89
|
||||
OpStore %90 %uint_10
|
||||
%92 = OpIAdd %uint %83 %uint_1
|
||||
%93 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %92
|
||||
OpStore %93 %uint_23
|
||||
%94 = OpIAdd %uint %83 %uint_2
|
||||
%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94
|
||||
OpStore %95 %71
|
||||
%98 = OpIAdd %uint %83 %uint_3
|
||||
%99 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %98
|
||||
OpStore %99 %uint_5
|
||||
%103 = OpLoad %v3uint %gl_GlobalInvocationID
|
||||
%104 = OpCompositeExtract %uint %103 0
|
||||
%105 = OpCompositeExtract %uint %103 1
|
||||
%106 = OpCompositeExtract %uint %103 2
|
||||
%107 = OpIAdd %uint %83 %uint_4
|
||||
%108 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %107
|
||||
OpStore %108 %104
|
||||
%109 = OpIAdd %uint %83 %uint_5
|
||||
%110 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %109
|
||||
OpStore %110 %105
|
||||
%112 = OpIAdd %uint %83 %uint_6
|
||||
%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112
|
||||
OpStore %113 %106
|
||||
%115 = OpIAdd %uint %83 %uint_7
|
||||
%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115
|
||||
OpStore %116 %72
|
||||
%118 = OpIAdd %uint %83 %uint_8
|
||||
%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118
|
||||
OpStore %119 %73
|
||||
%121 = OpIAdd %uint %83 %uint_9
|
||||
%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121
|
||||
OpStore %122 %74
|
||||
OpBranch %87
|
||||
%87 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndCheck<InstBuffAddrCheckPass>(
|
||||
defs_before + func_before, defs_after + func_after + new_funcs, true,
|
||||
true, 7u, 23u, 2u);
|
||||
}
|
||||
|
||||
TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
|
||||
// #version 450
|
||||
// #extension GL_EXT_buffer_reference : enable
|
||||
|
||||
// // forward reference
|
||||
// layout(buffer_reference) buffer blockType;
|
||||
|
||||
// layout(buffer_reference, std430, buffer_reference_align = 16) buffer
|
||||
// blockType {
|
||||
// int x;
|
||||
// blockType next;
|
||||
// };
|
||||
|
||||
// layout(std430) buffer rootBlock {
|
||||
// blockType root;
|
||||
// } r;
|
||||
|
||||
// void main()
|
||||
// {
|
||||
// blockType b = r.root;
|
||||
// b = b.next;
|
||||
// b.x = 531;
|
||||
// }
|
||||
|
||||
const std::string defs_before =
|
||||
R"(OpCapability Shader
|
||||
OpCapability PhysicalStorageBufferAddressesEXT
|
||||
OpExtension "SPV_EXT_physical_storage_buffer"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpSource GLSL 450
|
||||
OpSourceExtension "GL_EXT_buffer_reference"
|
||||
OpName %main "main"
|
||||
OpName %blockType "blockType"
|
||||
OpMemberName %blockType 0 "x"
|
||||
OpMemberName %blockType 1 "next"
|
||||
OpName %rootBlock "rootBlock"
|
||||
OpMemberName %rootBlock 0 "root"
|
||||
OpName %r "r"
|
||||
OpMemberDecorate %blockType 0 Offset 0
|
||||
OpMemberDecorate %blockType 1 Offset 8
|
||||
OpDecorate %blockType Block
|
||||
OpMemberDecorate %rootBlock 0 Offset 0
|
||||
OpDecorate %rootBlock Block
|
||||
OpDecorate %r DescriptorSet 0
|
||||
OpDecorate %r Binding 0
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
|
||||
%int = OpTypeInt 32 1
|
||||
%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
|
||||
%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
|
||||
%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
|
||||
%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
|
||||
%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
|
||||
%int_0 = OpConstant %int 0
|
||||
%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
|
||||
%int_1 = OpConstant %int 1
|
||||
%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
|
||||
%int_531 = OpConstant %int 531
|
||||
%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
|
||||
)";
|
||||
|
||||
const std::string defs_after =
|
||||
R"(OpCapability Shader
|
||||
OpCapability PhysicalStorageBufferAddressesEXT
|
||||
OpCapability Int64
|
||||
OpCapability Int64
|
||||
OpExtension "SPV_EXT_physical_storage_buffer"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpSource GLSL 450
|
||||
OpSourceExtension "GL_EXT_buffer_reference"
|
||||
OpName %main "main"
|
||||
OpName %blockType "blockType"
|
||||
OpMemberName %blockType 0 "x"
|
||||
OpMemberName %blockType 1 "next"
|
||||
OpName %rootBlock "rootBlock"
|
||||
OpMemberName %rootBlock 0 "root"
|
||||
OpName %r "r"
|
||||
OpMemberDecorate %blockType 0 Offset 0
|
||||
OpMemberDecorate %blockType 1 Offset 8
|
||||
OpDecorate %blockType Block
|
||||
OpMemberDecorate %rootBlock 0 Offset 0
|
||||
OpDecorate %rootBlock Block
|
||||
OpDecorate %r DescriptorSet 0
|
||||
OpDecorate %r Binding 0
|
||||
OpDecorate %_runtimearr_ulong ArrayStride 8
|
||||
OpDecorate %_struct_45 Block
|
||||
OpMemberDecorate %_struct_45 0 Offset 0
|
||||
OpDecorate %47 DescriptorSet 7
|
||||
OpDecorate %47 Binding 2
|
||||
OpDecorate %_runtimearr_uint ArrayStride 4
|
||||
OpDecorate %_struct_84 Block
|
||||
OpMemberDecorate %_struct_84 0 Offset 0
|
||||
OpMemberDecorate %_struct_84 1 Offset 4
|
||||
OpDecorate %86 DescriptorSet 7
|
||||
OpDecorate %86 Binding 0
|
||||
OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
|
||||
%int = OpTypeInt 32 1
|
||||
%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
|
||||
%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
|
||||
%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
|
||||
%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
|
||||
%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
|
||||
%int_0 = OpConstant %int 0
|
||||
%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
|
||||
%int_1 = OpConstant %int 1
|
||||
%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
|
||||
%int_531 = OpConstant %int 531
|
||||
%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_2 = OpConstant %uint 2
|
||||
%ulong = OpTypeInt 64 0
|
||||
%uint_8 = OpConstant %uint 8
|
||||
%bool = OpTypeBool
|
||||
%34 = OpTypeFunction %bool %ulong %uint
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%_runtimearr_ulong = OpTypeRuntimeArray %ulong
|
||||
%_struct_45 = OpTypeStruct %_runtimearr_ulong
|
||||
%_ptr_StorageBuffer__struct_45 = OpTypePointer StorageBuffer %_struct_45
|
||||
%47 = OpVariable %_ptr_StorageBuffer__struct_45 StorageBuffer
|
||||
%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%uint_32 = OpConstant %uint 32
|
||||
%77 = OpTypeFunction %void %uint %uint %uint %uint
|
||||
%_runtimearr_uint = OpTypeRuntimeArray %uint
|
||||
%_struct_84 = OpTypeStruct %uint %_runtimearr_uint
|
||||
%_ptr_StorageBuffer__struct_84 = OpTypePointer StorageBuffer %_struct_84
|
||||
%86 = OpVariable %_ptr_StorageBuffer__struct_84 StorageBuffer
|
||||
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
|
||||
%uint_10 = OpConstant %uint 10
|
||||
%uint_4 = OpConstant %uint 4
|
||||
%uint_23 = OpConstant %uint 23
|
||||
%uint_5 = OpConstant %uint 5
|
||||
%uint_3 = OpConstant %uint 3
|
||||
%v3uint = OpTypeVector %uint 3
|
||||
%_ptr_Input_v3uint = OpTypePointer Input %v3uint
|
||||
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
||||
%uint_6 = OpConstant %uint 6
|
||||
%uint_7 = OpConstant %uint 7
|
||||
%uint_9 = OpConstant %uint 9
|
||||
%uint_44 = OpConstant %uint 44
|
||||
%132 = OpConstantNull %_ptr_PhysicalStorageBufferEXT_blockType
|
||||
%uint_46 = OpConstant %uint 46
|
||||
)";
|
||||
|
||||
const std::string func_before =
|
||||
R"(%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
|
||||
%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
|
||||
%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
|
||||
%22 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
|
||||
%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %22 %int_0
|
||||
OpStore %26 %int_531 Aligned 16
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string func_after =
|
||||
R"(%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
|
||||
%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
|
||||
%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
|
||||
%30 = OpConvertPtrToU %ulong %21
|
||||
%67 = OpFunctionCall %bool %32 %30 %uint_8
|
||||
OpSelectionMerge %68 None
|
||||
OpBranchConditional %67 %69 %70
|
||||
%69 = OpLabel
|
||||
%71 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
|
||||
OpBranch %68
|
||||
%70 = OpLabel
|
||||
%72 = OpUConvert %uint %30
|
||||
%74 = OpShiftRightLogical %ulong %30 %uint_32
|
||||
%75 = OpUConvert %uint %74
|
||||
%131 = OpFunctionCall %void %76 %uint_44 %uint_2 %72 %75
|
||||
OpBranch %68
|
||||
%68 = OpLabel
|
||||
%133 = OpPhi %_ptr_PhysicalStorageBufferEXT_blockType %71 %69 %132 %70
|
||||
%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %133 %int_0
|
||||
%134 = OpConvertPtrToU %ulong %26
|
||||
%135 = OpFunctionCall %bool %32 %134 %uint_4
|
||||
OpSelectionMerge %136 None
|
||||
OpBranchConditional %135 %137 %138
|
||||
%137 = OpLabel
|
||||
OpStore %26 %int_531 Aligned 16
|
||||
OpBranch %136
|
||||
%138 = OpLabel
|
||||
%139 = OpUConvert %uint %134
|
||||
%140 = OpShiftRightLogical %ulong %134 %uint_32
|
||||
%141 = OpUConvert %uint %140
|
||||
%143 = OpFunctionCall %void %76 %uint_46 %uint_2 %139 %141
|
||||
OpBranch %136
|
||||
%136 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string new_funcs =
|
||||
R"(%32 = OpFunction %bool None %34
|
||||
%35 = OpFunctionParameter %ulong
|
||||
%36 = OpFunctionParameter %uint
|
||||
%37 = OpLabel
|
||||
OpBranch %38
|
||||
%38 = OpLabel
|
||||
%40 = OpPhi %uint %uint_1 %37 %41 %39
|
||||
OpLoopMerge %43 %39 None
|
||||
OpBranch %39
|
||||
%39 = OpLabel
|
||||
%41 = OpIAdd %uint %40 %uint_1
|
||||
%50 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %41
|
||||
%51 = OpLoad %ulong %50
|
||||
%52 = OpUGreaterThan %bool %51 %35
|
||||
OpBranchConditional %52 %43 %38
|
||||
%43 = OpLabel
|
||||
%53 = OpISub %uint %41 %uint_1
|
||||
%54 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %53
|
||||
%55 = OpLoad %ulong %54
|
||||
%56 = OpISub %ulong %35 %55
|
||||
%57 = OpUConvert %ulong %36
|
||||
%58 = OpIAdd %ulong %56 %57
|
||||
%59 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %uint_0
|
||||
%60 = OpLoad %ulong %59
|
||||
%61 = OpUConvert %uint %60
|
||||
%62 = OpISub %uint %53 %uint_1
|
||||
%63 = OpIAdd %uint %62 %61
|
||||
%64 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %63
|
||||
%65 = OpLoad %ulong %64
|
||||
%66 = OpULessThanEqual %bool %58 %65
|
||||
OpReturnValue %66
|
||||
OpFunctionEnd
|
||||
%76 = OpFunction %void None %77
|
||||
%78 = OpFunctionParameter %uint
|
||||
%79 = OpFunctionParameter %uint
|
||||
%80 = OpFunctionParameter %uint
|
||||
%81 = OpFunctionParameter %uint
|
||||
%82 = OpLabel
|
||||
%88 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_0
|
||||
%91 = OpAtomicIAdd %uint %88 %uint_4 %uint_0 %uint_10
|
||||
%92 = OpIAdd %uint %91 %uint_10
|
||||
%93 = OpArrayLength %uint %86 1
|
||||
%94 = OpULessThanEqual %bool %92 %93
|
||||
OpSelectionMerge %95 None
|
||||
OpBranchConditional %94 %96 %95
|
||||
%96 = OpLabel
|
||||
%97 = OpIAdd %uint %91 %uint_0
|
||||
%98 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %97
|
||||
OpStore %98 %uint_10
|
||||
%100 = OpIAdd %uint %91 %uint_1
|
||||
%101 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %100
|
||||
OpStore %101 %uint_23
|
||||
%102 = OpIAdd %uint %91 %uint_2
|
||||
%103 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %102
|
||||
OpStore %103 %78
|
||||
%106 = OpIAdd %uint %91 %uint_3
|
||||
%107 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %106
|
||||
OpStore %107 %uint_5
|
||||
%111 = OpLoad %v3uint %gl_GlobalInvocationID
|
||||
%112 = OpCompositeExtract %uint %111 0
|
||||
%113 = OpCompositeExtract %uint %111 1
|
||||
%114 = OpCompositeExtract %uint %111 2
|
||||
%115 = OpIAdd %uint %91 %uint_4
|
||||
%116 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %115
|
||||
OpStore %116 %112
|
||||
%117 = OpIAdd %uint %91 %uint_5
|
||||
%118 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %117
|
||||
OpStore %118 %113
|
||||
%120 = OpIAdd %uint %91 %uint_6
|
||||
%121 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %120
|
||||
OpStore %121 %114
|
||||
%123 = OpIAdd %uint %91 %uint_7
|
||||
%124 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %123
|
||||
OpStore %124 %79
|
||||
%125 = OpIAdd %uint %91 %uint_8
|
||||
%126 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %125
|
||||
OpStore %126 %80
|
||||
%128 = OpIAdd %uint %91 %uint_9
|
||||
%129 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %128
|
||||
OpStore %129 %81
|
||||
OpBranch %95
|
||||
%95 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndCheck<InstBuffAddrCheckPass>(
|
||||
defs_before + func_before, defs_after + func_after + new_funcs, true,
|
||||
true, 7u, 23u, 2u);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
21
3rdparty/spirv-tools/test/opt/pass_fixture.h
vendored
21
3rdparty/spirv-tools/test/opt/pass_fixture.h
vendored
@ -75,7 +75,9 @@ class PassTest : public TestT {
|
||||
const auto status = pass->Run(context());
|
||||
|
||||
std::vector<uint32_t> binary;
|
||||
context()->module()->ToBinary(&binary, skip_nop);
|
||||
if (status != Pass::Status::Failure) {
|
||||
context()->module()->ToBinary(&binary, skip_nop);
|
||||
}
|
||||
return std::make_tuple(binary, status);
|
||||
}
|
||||
|
||||
@ -241,15 +243,18 @@ class PassTest : public TestT {
|
||||
context()->set_preserve_spec_constants(
|
||||
OptimizerOptions()->preserve_spec_constants_);
|
||||
|
||||
manager_->Run(context());
|
||||
auto status = manager_->Run(context());
|
||||
EXPECT_NE(status, Pass::Status::Failure);
|
||||
|
||||
std::vector<uint32_t> binary;
|
||||
context()->module()->ToBinary(&binary, /* skip_nop = */ false);
|
||||
if (status != Pass::Status::Failure) {
|
||||
std::vector<uint32_t> binary;
|
||||
context()->module()->ToBinary(&binary, /* skip_nop = */ false);
|
||||
|
||||
std::string optimized;
|
||||
SpirvTools tools(env_);
|
||||
EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_));
|
||||
EXPECT_EQ(expected, optimized);
|
||||
std::string optimized;
|
||||
SpirvTools tools(env_);
|
||||
EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_));
|
||||
EXPECT_EQ(expected, optimized);
|
||||
}
|
||||
}
|
||||
|
||||
void SetAssembleOptions(uint32_t assemble_options) {
|
||||
|
@ -419,6 +419,39 @@ OpFunctionEnd
|
||||
SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(PrivateToLocalTest, IdBoundOverflow1) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginLowerLeft
|
||||
OpSource HLSL 84
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypeVector %6 4
|
||||
%8 = OpTypeStruct %7
|
||||
%4194302 = OpTypeStruct %8 %8
|
||||
%9 = OpTypeStruct %8 %8
|
||||
%11 = OpTypePointer Private %7
|
||||
%18 = OpTypeStruct %6 %9
|
||||
%12 = OpVariable %11 Private
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpLoad %7 %12
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result = SinglePassRunToBinary<PrivateToLocalPass>(text, true);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -1621,7 +1621,7 @@ TEST_F(ScalarReplacementTest, TestAccessChainWithNoIndexes) {
|
||||
}
|
||||
|
||||
// Test that id overflow is handled gracefully.
|
||||
TEST_F(ScalarReplacementTest, IdBoundOverflow) {
|
||||
TEST_F(ScalarReplacementTest, IdBoundOverflow1) {
|
||||
const std::string text = R"(
|
||||
OpCapability ImageQuery
|
||||
OpMemoryModel Logical GLSL450
|
||||
@ -1652,8 +1652,103 @@ OpFunctionEnd
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result =
|
||||
SinglePassRunAndDisassemble<ScalarReplacementPass>(text, true, false);
|
||||
auto result = SinglePassRunToBinary<ScalarReplacementPass>(text, true, false);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
// Test that id overflow is handled gracefully.
|
||||
TEST_F(ScalarReplacementTest, IdBoundOverflow2) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main" %17
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypeVector %6 4
|
||||
%8 = OpTypeStruct %7
|
||||
%9 = OpTypePointer Function %8
|
||||
%16 = OpTypePointer Output %7
|
||||
%21 = OpTypeInt 32 1
|
||||
%22 = OpConstant %21 0
|
||||
%23 = OpTypePointer Function %7
|
||||
%17 = OpVariable %16 Output
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%4194300 = OpVariable %23 Function
|
||||
%10 = OpVariable %9 Function
|
||||
%4194301 = OpAccessChain %23 %10 %22
|
||||
%4194302 = OpLoad %7 %4194301
|
||||
OpStore %4194300 %4194302
|
||||
%15 = OpLoad %7 %4194300
|
||||
OpStore %17 %15
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result = SinglePassRunToBinary<ScalarReplacementPass>(text, true, false);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
// Test that id overflow is handled gracefully.
|
||||
TEST_F(ScalarReplacementTest, IdBoundOverflow3) {
|
||||
const std::string text = R"(
|
||||
OpCapability InterpolationFunction
|
||||
OpExtension "z"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypeStruct %6 %6
|
||||
%9 = OpTypePointer Function %7
|
||||
%18 = OpTypeFunction %7 %9
|
||||
%21 = OpTypeInt 32 0
|
||||
%22 = OpConstant %21 4293000676
|
||||
%4194302 = OpConstantNull %6
|
||||
%4 = OpFunction %2 Inline|Pure %3
|
||||
%786464 = OpLabel
|
||||
%4194298 = OpVariable %9 Function
|
||||
%10 = OpVariable %9 Function
|
||||
%4194299 = OpUDiv %21 %22 %22
|
||||
%4194300 = OpLoad %7 %10
|
||||
%50959 = OpLoad %7 %4194298
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
%1 = OpFunction %7 None %18
|
||||
%19 = OpFunctionParameter %9
|
||||
%147667 = OpLabel
|
||||
%2044391 = OpUDiv %21 %22 %22
|
||||
%25 = OpLoad %7 %19
|
||||
OpReturnValue %25
|
||||
OpFunctionEnd
|
||||
%4194295 = OpFunction %2 None %3
|
||||
%4194296 = OpLabel
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result = SinglePassRunToBinary<ScalarReplacementPass>(text, true, false);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
|
54
3rdparty/spirv-tools/test/opt/types_test.cpp
vendored
54
3rdparty/spirv-tools/test/opt/types_test.cpp
vendored
@ -65,17 +65,18 @@ class SameTypeTest : public ::testing::Test {
|
||||
#define TestMultipleInstancesOfTheSameType(ty, ...) \
|
||||
TestMultipleInstancesOfTheSameTypeQualified(ty, Simple, __VA_ARGS__)
|
||||
|
||||
TestMultipleInstancesOfTheSameType(Void);
|
||||
TestMultipleInstancesOfTheSameType(Bool);
|
||||
TestMultipleInstancesOfTheSameType(Integer, 32, true);
|
||||
TestMultipleInstancesOfTheSameType(Float, 64);
|
||||
TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3);
|
||||
TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4);
|
||||
// clang-format off
|
||||
TestMultipleInstancesOfTheSameType(Void)
|
||||
TestMultipleInstancesOfTheSameType(Bool)
|
||||
TestMultipleInstancesOfTheSameType(Integer, 32, true)
|
||||
TestMultipleInstancesOfTheSameType(Float, 64)
|
||||
TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3)
|
||||
TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4)
|
||||
TestMultipleInstancesOfTheSameType(Image, f64_t_.get(), SpvDimCube, 0, 0, 1, 1,
|
||||
SpvImageFormatRgb10A2,
|
||||
SpvAccessQualifierWriteOnly);
|
||||
TestMultipleInstancesOfTheSameType(Sampler);
|
||||
TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get());
|
||||
SpvAccessQualifierWriteOnly)
|
||||
TestMultipleInstancesOfTheSameType(Sampler)
|
||||
TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get())
|
||||
// There are three classes of arrays, based on the kinds of length information
|
||||
// they have.
|
||||
// 1. Array length is a constant or spec constant without spec ID, with literals
|
||||
@ -85,34 +86,35 @@ TestMultipleInstancesOfTheSameTypeQualified(Array, LenConstant, u32_t_.get(),
|
||||
{
|
||||
0,
|
||||
9999,
|
||||
}});
|
||||
}})
|
||||
// 2. Array length is a spec constant with a given spec id.
|
||||
TestMultipleInstancesOfTheSameTypeQualified(Array, LenSpecId, u32_t_.get(),
|
||||
Array::LengthInfo{42, {1, 99}});
|
||||
Array::LengthInfo{42, {1, 99}})
|
||||
// 3. Array length is an OpSpecConstantOp expression
|
||||
TestMultipleInstancesOfTheSameTypeQualified(Array, LenDefiningId, u32_t_.get(),
|
||||
Array::LengthInfo{42, {2, 42}});
|
||||
Array::LengthInfo{42, {2, 42}})
|
||||
|
||||
TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get());
|
||||
TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get())
|
||||
TestMultipleInstancesOfTheSameType(Struct, std::vector<const Type*>{
|
||||
u32_t_.get(), f64_t_.get()});
|
||||
TestMultipleInstancesOfTheSameType(Opaque, "testing rocks");
|
||||
TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), SpvStorageClassInput);
|
||||
u32_t_.get(), f64_t_.get()})
|
||||
TestMultipleInstancesOfTheSameType(Opaque, "testing rocks")
|
||||
TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), SpvStorageClassInput)
|
||||
TestMultipleInstancesOfTheSameType(Function, u32_t_.get(),
|
||||
{f64_t_.get(), f64_t_.get()});
|
||||
TestMultipleInstancesOfTheSameType(Event);
|
||||
TestMultipleInstancesOfTheSameType(DeviceEvent);
|
||||
TestMultipleInstancesOfTheSameType(ReserveId);
|
||||
TestMultipleInstancesOfTheSameType(Queue);
|
||||
TestMultipleInstancesOfTheSameType(Pipe, SpvAccessQualifierReadWrite);
|
||||
TestMultipleInstancesOfTheSameType(ForwardPointer, 10, SpvStorageClassUniform);
|
||||
TestMultipleInstancesOfTheSameType(PipeStorage);
|
||||
TestMultipleInstancesOfTheSameType(NamedBarrier);
|
||||
TestMultipleInstancesOfTheSameType(AccelerationStructureNV);
|
||||
{f64_t_.get(), f64_t_.get()})
|
||||
TestMultipleInstancesOfTheSameType(Event)
|
||||
TestMultipleInstancesOfTheSameType(DeviceEvent)
|
||||
TestMultipleInstancesOfTheSameType(ReserveId)
|
||||
TestMultipleInstancesOfTheSameType(Queue)
|
||||
TestMultipleInstancesOfTheSameType(Pipe, SpvAccessQualifierReadWrite)
|
||||
TestMultipleInstancesOfTheSameType(ForwardPointer, 10, SpvStorageClassUniform)
|
||||
TestMultipleInstancesOfTheSameType(PipeStorage)
|
||||
TestMultipleInstancesOfTheSameType(NamedBarrier)
|
||||
TestMultipleInstancesOfTheSameType(AccelerationStructureNV)
|
||||
#undef TestMultipleInstanceOfTheSameType
|
||||
#undef TestMultipleInstanceOfTheSameTypeQual
|
||||
|
||||
std::vector<std::unique_ptr<Type>> GenerateAllTypes() {
|
||||
// clang-format on
|
||||
// Types in this test case are only equal to themselves, nothing else.
|
||||
std::vector<std::unique_ptr<Type>> types;
|
||||
|
||||
|
267
3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp
vendored
Normal file
267
3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
// Copyright (c) 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "test/opt/assembly_builder.h"
|
||||
#include "test/opt/pass_fixture.h"
|
||||
#include "test/opt/pass_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
using WrapOpKillTest = PassTest<::testing::Test>;
|
||||
|
||||
TEST_F(WrapOpKillTest, SingleOpKill) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpEntryPoint Fragment [[main:%\w+]]
|
||||
; CHECK: [[main]] = OpFunction
|
||||
; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
|
||||
; CHECK: [[orig_kill]] = OpFunction
|
||||
; CHECK-NEXT: OpLabel
|
||||
; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
|
||||
; CHECK-NEXT: OpUnreachable
|
||||
; CHECK: [[new_kill]] = OpFunction
|
||||
; CHECK-NEXT: OpLabel
|
||||
; CHECK-NEXT: OpKill
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 330
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%5 = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%true = OpConstantTrue %bool
|
||||
%main = OpFunction %void None %5
|
||||
%8 = OpLabel
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
OpLoopMerge %10 %11 None
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranchConditional %true %13 %10
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
%14 = OpFunctionCall %void %kill_
|
||||
OpBranch %9
|
||||
%10 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%kill_ = OpFunction %void None %5
|
||||
%15 = OpLabel
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<WrapOpKill>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(WrapOpKillTest, MultipleOpKillInSameFunc) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpEntryPoint Fragment [[main:%\w+]]
|
||||
; CHECK: [[main]] = OpFunction
|
||||
; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
|
||||
; CHECK: [[orig_kill]] = OpFunction
|
||||
; CHECK-NEXT: OpLabel
|
||||
; CHECK-NEXT: OpSelectionMerge
|
||||
; CHECK-NEXT: OpBranchConditional
|
||||
; CHECK-NEXT: OpLabel
|
||||
; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
|
||||
; CHECK-NEXT: OpUnreachable
|
||||
; CHECK-NEXT: OpLabel
|
||||
; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
|
||||
; CHECK-NEXT: OpUnreachable
|
||||
; CHECK: [[new_kill]] = OpFunction
|
||||
; CHECK-NEXT: OpLabel
|
||||
; CHECK-NEXT: OpKill
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 330
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%5 = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%true = OpConstantTrue %bool
|
||||
%main = OpFunction %void None %5
|
||||
%8 = OpLabel
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
OpLoopMerge %10 %11 None
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranchConditional %true %13 %10
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
%14 = OpFunctionCall %void %kill_
|
||||
OpBranch %9
|
||||
%10 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%kill_ = OpFunction %void None %5
|
||||
%15 = OpLabel
|
||||
OpSelectionMerge %16 None
|
||||
OpBranchConditional %true %17 %18
|
||||
%17 = OpLabel
|
||||
OpKill
|
||||
%18 = OpLabel
|
||||
OpKill
|
||||
%16 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<WrapOpKill>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(WrapOpKillTest, MultipleOpKillInDifferentFunc) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpEntryPoint Fragment [[main:%\w+]]
|
||||
; CHECK: [[main]] = OpFunction
|
||||
; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]]
|
||||
; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]]
|
||||
; CHECK: [[orig_kill1]] = OpFunction
|
||||
; CHECK-NEXT: OpLabel
|
||||
; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
|
||||
; CHECK-NEXT: OpUnreachable
|
||||
; CHECK: [[orig_kill2]] = OpFunction
|
||||
; CHECK-NEXT: OpLabel
|
||||
; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
|
||||
; CHECK-NEXT: OpUnreachable
|
||||
; CHECK: [[new_kill]] = OpFunction
|
||||
; CHECK-NEXT: OpLabel
|
||||
; CHECK-NEXT: OpKill
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 330
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%true = OpConstantTrue %bool
|
||||
%main = OpFunction %void None %4
|
||||
%7 = OpLabel
|
||||
OpBranch %8
|
||||
%8 = OpLabel
|
||||
OpLoopMerge %9 %10 None
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpBranchConditional %true %12 %9
|
||||
%12 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
%13 = OpFunctionCall %void %14
|
||||
%15 = OpFunctionCall %void %16
|
||||
OpBranch %8
|
||||
%9 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%14 = OpFunction %void None %4
|
||||
%17 = OpLabel
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
%16 = OpFunction %void None %4
|
||||
%18 = OpLabel
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<WrapOpKill>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(WrapOpKillTest, IdBoundOverflow1) {
|
||||
const std::string text = R"(
|
||||
OpCapability GeometryStreams
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%4 = OpFunction %2 Pure|Const %3
|
||||
%4194302 = OpLabel
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
TEST_F(WrapOpKillTest, IdBoundOverflow2) {
|
||||
const std::string text = R"(
|
||||
OpCapability GeometryStreams
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%4 = OpFunction %2 Pure|Const %3
|
||||
%4194301 = OpLabel
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
TEST_F(WrapOpKillTest, IdBoundOverflow3) {
|
||||
const std::string text = R"(
|
||||
OpCapability GeometryStreams
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%4 = OpFunction %2 Pure|Const %3
|
||||
%4194300 = OpLabel
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
@ -24,6 +24,7 @@ add_spvtools_unittest(TARGET reduce
|
||||
remove_block_test.cpp
|
||||
remove_function_test.cpp
|
||||
remove_opname_instruction_test.cpp
|
||||
remove_relaxed_precision_decoration_test.cpp
|
||||
remove_selection_test.cpp
|
||||
remove_unreferenced_instruction_test.cpp
|
||||
structured_loop_to_selection_test.cpp
|
||||
|
177
3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp
vendored
Normal file
177
3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
// Copyright (c) 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
#include "source/reduce/reduction_pass.h"
|
||||
#include "test/reduce/reduce_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
TEST(RemoveRelaxedPrecisionDecorationTest, NothingToRemove) {
|
||||
const std::string source = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, source, kReduceAssembleOption);
|
||||
const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(RemoveRelaxedPrecisionDecorationTest, RemoveDecorations) {
|
||||
const std::string source = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %8 "f"
|
||||
OpName %12 "i"
|
||||
OpName %16 "v"
|
||||
OpName %19 "S"
|
||||
OpMemberName %19 0 "a"
|
||||
OpMemberName %19 1 "b"
|
||||
OpMemberName %19 2 "c"
|
||||
OpName %21 "s"
|
||||
OpDecorate %8 RelaxedPrecision
|
||||
OpDecorate %12 RelaxedPrecision
|
||||
OpDecorate %16 RelaxedPrecision
|
||||
OpDecorate %17 RelaxedPrecision
|
||||
OpDecorate %18 RelaxedPrecision
|
||||
OpMemberDecorate %19 0 RelaxedPrecision
|
||||
OpMemberDecorate %19 1 RelaxedPrecision
|
||||
OpMemberDecorate %19 2 RelaxedPrecision
|
||||
OpDecorate %22 RelaxedPrecision
|
||||
OpDecorate %23 RelaxedPrecision
|
||||
OpDecorate %24 RelaxedPrecision
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 2
|
||||
%10 = OpTypeInt 32 1
|
||||
%11 = OpTypePointer Function %10
|
||||
%13 = OpConstant %10 22
|
||||
%14 = OpTypeVector %6 2
|
||||
%15 = OpTypePointer Function %14
|
||||
%19 = OpTypeStruct %10 %6 %14
|
||||
%20 = OpTypePointer Function %19
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%12 = OpVariable %11 Function
|
||||
%16 = OpVariable %15 Function
|
||||
%21 = OpVariable %20 Function
|
||||
OpStore %8 %9
|
||||
OpStore %12 %13
|
||||
%17 = OpLoad %6 %8
|
||||
%18 = OpCompositeConstruct %14 %17 %17
|
||||
OpStore %16 %18
|
||||
%22 = OpLoad %10 %12
|
||||
%23 = OpLoad %6 %8
|
||||
%24 = OpLoad %14 %16
|
||||
%25 = OpCompositeConstruct %19 %22 %23 %24
|
||||
OpStore %21 %25
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, source, kReduceAssembleOption);
|
||||
const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(11, ops.size());
|
||||
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
}
|
||||
|
||||
const std::string expected = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %8 "f"
|
||||
OpName %12 "i"
|
||||
OpName %16 "v"
|
||||
OpName %19 "S"
|
||||
OpMemberName %19 0 "a"
|
||||
OpMemberName %19 1 "b"
|
||||
OpMemberName %19 2 "c"
|
||||
OpName %21 "s"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 2
|
||||
%10 = OpTypeInt 32 1
|
||||
%11 = OpTypePointer Function %10
|
||||
%13 = OpConstant %10 22
|
||||
%14 = OpTypeVector %6 2
|
||||
%15 = OpTypePointer Function %14
|
||||
%19 = OpTypeStruct %10 %6 %14
|
||||
%20 = OpTypePointer Function %19
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%12 = OpVariable %11 Function
|
||||
%16 = OpVariable %15 Function
|
||||
%21 = OpVariable %20 Function
|
||||
OpStore %8 %9
|
||||
OpStore %12 %13
|
||||
%17 = OpLoad %6 %8
|
||||
%18 = OpCompositeConstruct %14 %17 %17
|
||||
OpStore %16 %18
|
||||
%22 = OpLoad %10 %12
|
||||
%23 = OpLoad %6 %8
|
||||
%24 = OpLoad %14 %16
|
||||
%25 = OpCompositeConstruct %19 %22 %23 %24
|
||||
OpStore %21 %25
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(env, expected, context.get());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
6
3rdparty/spirv-tools/test/tools/opt/flags.py
vendored
6
3rdparty/spirv-tools/test/tools/opt/flags.py
vendored
@ -57,7 +57,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4,
|
||||
"""Tests that spirv-opt accepts all valid optimization flags."""
|
||||
|
||||
flags = [
|
||||
'--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids',
|
||||
'--wrap-opkill', '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids',
|
||||
'--convert-local-access-chains', '--copy-propagate-arrays',
|
||||
'--eliminate-dead-branches',
|
||||
'--eliminate-dead-code-aggressive', '--eliminate-dead-const',
|
||||
@ -76,6 +76,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4,
|
||||
'--unify-const'
|
||||
]
|
||||
expected_passes = [
|
||||
'wrap-opkill',
|
||||
'ccp',
|
||||
'cfg-cleanup',
|
||||
'combine-access-chains',
|
||||
@ -134,6 +135,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_4,
|
||||
|
||||
flags = ['-O']
|
||||
expected_passes = [
|
||||
'wrap-opkill',
|
||||
'eliminate-dead-branches',
|
||||
'merge-return',
|
||||
'inline-entry-points-exhaustive',
|
||||
@ -181,6 +183,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_4,
|
||||
|
||||
flags = ['-Os']
|
||||
expected_passes = [
|
||||
'wrap-opkill',
|
||||
'eliminate-dead-branches',
|
||||
'merge-return',
|
||||
'inline-entry-points-exhaustive',
|
||||
@ -221,6 +224,7 @@ class TestLegalizationPasses(expect.ValidObjectFile1_4,
|
||||
|
||||
flags = ['--legalize-hlsl']
|
||||
expected_passes = [
|
||||
'wrap-opkill',
|
||||
'eliminate-dead-branches',
|
||||
'merge-return',
|
||||
'inline-entry-points-exhaustive',
|
||||
|
@ -442,6 +442,22 @@ OpMemoryModel Logical GLSL450
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateConstant, NullMatrix) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%float = OpTypeFloat 32
|
||||
%v2float = OpTypeVector %float 2
|
||||
%mat2x2 = OpTypeMatrix %v2float 2
|
||||
%null_vector = OpConstantNull %v2float
|
||||
%null_matrix = OpConstantComposite %mat2x2 %null_vector %null_vector
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
@ -5315,10 +5315,10 @@ INSTANTIATE_TEST_SUITE_P(AllFractLike, ValidateOpenCLStdFractLike,
|
||||
|
||||
TEST_F(ValidateExtInst, OpenCLStdRemquoSuccess) {
|
||||
const std::string body = R"(
|
||||
%var_f32 = OpVariable %f32_ptr_function Function
|
||||
%var_f32vec2 = OpVariable %f32vec2_ptr_function Function
|
||||
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_f32
|
||||
%val2 = OpExtInst %f32vec2 %extinst remquo %f32vec2_01 %f32vec2_12 %var_f32vec2
|
||||
%var_u32 = OpVariable %u32_ptr_function Function
|
||||
%var_u32vec2 = OpVariable %u32vec2_ptr_function Function
|
||||
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32
|
||||
%val2 = OpExtInst %f32vec2 %extinst remquo %f32vec2_01 %f32vec2_12 %var_u32vec2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body));
|
||||
@ -5327,8 +5327,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoSuccess) {
|
||||
|
||||
TEST_F(ValidateExtInst, OpenCLStdRemquoIntResultType) {
|
||||
const std::string body = R"(
|
||||
%var_f32 = OpVariable %f32_ptr_function Function
|
||||
%val1 = OpExtInst %u32 %extinst remquo %f32_3 %f32_2 %var_f32
|
||||
%var_u32 = OpVariable %u32_ptr_function Function
|
||||
%val1 = OpExtInst %u32 %extinst remquo %f32_3 %f32_2 %var_u32
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body));
|
||||
@ -5341,8 +5341,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoIntResultType) {
|
||||
|
||||
TEST_F(ValidateExtInst, OpenCLStdRemquoXWrongType) {
|
||||
const std::string body = R"(
|
||||
%var_f32 = OpVariable %f32_ptr_function Function
|
||||
%val1 = OpExtInst %f32 %extinst remquo %u32_3 %f32_2 %var_f32
|
||||
%var_u32 = OpVariable %f32_ptr_function Function
|
||||
%val1 = OpExtInst %f32 %extinst remquo %u32_3 %f32_2 %var_u32
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body));
|
||||
@ -5355,8 +5355,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoXWrongType) {
|
||||
|
||||
TEST_F(ValidateExtInst, OpenCLStdRemquoYWrongType) {
|
||||
const std::string body = R"(
|
||||
%var_f32 = OpVariable %f32_ptr_function Function
|
||||
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %u32_2 %var_f32
|
||||
%var_u32 = OpVariable %f32_ptr_function Function
|
||||
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %u32_2 %var_u32
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body));
|
||||
@ -5395,17 +5395,44 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongStorageClass) {
|
||||
|
||||
TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataType) {
|
||||
const std::string body = R"(
|
||||
%var_u32 = OpVariable %u32_ptr_function Function
|
||||
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32
|
||||
%var_f32 = OpVariable %f32_ptr_function Function
|
||||
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_f32
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpenCL.std remquo: "
|
||||
"expected data type of the pointer to be a 32-bit int "
|
||||
"scalar or vector type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataTypeWidth) {
|
||||
const std::string body = R"(
|
||||
%var_u64 = OpVariable %u64_ptr_function Function
|
||||
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u64
|
||||
)";
|
||||
CompileSuccessfully(GenerateKernelCode(body));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpenCL.std remquo: "
|
||||
"expected data type of the pointer to be a 32-bit int "
|
||||
"scalar or vector type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongNumberOfComponents) {
|
||||
const std::string body = R"(
|
||||
%var_u32vec2 = OpVariable %u32vec2_ptr_function Function
|
||||
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32vec2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"OpenCL.std remquo: "
|
||||
"expected data type of the pointer to be equal to Result Type"));
|
||||
HasSubstr("OpenCL.std remquo: "
|
||||
"expected data type of the pointer to have the same number "
|
||||
"of components as Result Type"));
|
||||
}
|
||||
|
||||
TEST_P(ValidateOpenCLStdFrexpLike, Success) {
|
||||
|
@ -1715,7 +1715,7 @@ TEST_F(ValidateIdWithMessage,
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpSpecConstantComposite Constituent <id> '7[%7]' is "
|
||||
"not a constant composite or undef."));
|
||||
"not a constant or undef."));
|
||||
}
|
||||
|
||||
// Invalid: Composite contains a column that is *not* a vector (it's an array)
|
||||
|
Loading…
Reference in New Issue
Block a user