Add base and core bindless validation instrumentation classes (#2014) * Add base and core bindless validation instrumentation classes * Fix formatting. * Few more formatting fixes * Fix build failure * More build fixes * Need to call non-const functions in order. Specifically, these are functions which call TakeNextId(). These need to be called in a specific order to guarantee that tests which do exact compares will work across all platforms. c++ pretty much does not guarantee order of evaluation of operands, so any such functions need to be called separately in individual statements to guarantee order. * More ordering. * And more ordering. * And more formatting. * Attempt to fix NDK build * Another attempt to address NDK build problem. * One more attempt at NDK build failure * Add instrument.hpp to BUILD.gn * Some name improvement in instrument.hpp * Change all types in instrument.hpp to int. * Improve documentation in instrument.hpp * Format fixes * Comment clean up in instrument.hpp * imageInst -> image_inst * Fix GetLabel() issue.
diff --git a/Android.mk b/Android.mk index 4e6025d..79a79f6 100644 --- a/Android.mk +++ b/Android.mk
@@ -102,8 +102,10 @@ source/opt/inline_pass.cpp \ source/opt/inline_exhaustive_pass.cpp \ source/opt/inline_opaque_pass.cpp \ + source/opt/inst_bindless_check_pass.cpp \ source/opt/instruction.cpp \ source/opt/instruction_list.cpp \ + source/opt/instrument_pass.cpp \ source/opt/ir_context.cpp \ source/opt/ir_loader.cpp \ source/opt/licm_pass.cpp \
diff --git a/BUILD.gn b/BUILD.gn index cf6286f..5a5b476 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -297,6 +297,7 @@ "include/spirv-tools/libspirv.hpp", "include/spirv-tools/linker.hpp", "include/spirv-tools/optimizer.hpp", + "include/spirv-tools/instrument.hpp", ] public_configs = [ ":spvtools_public_config" ] @@ -511,10 +512,14 @@ "source/opt/inline_opaque_pass.h", "source/opt/inline_pass.cpp", "source/opt/inline_pass.h", + "source/opt/inst_bindless_check_pass.cpp", + "source/opt/inst_bindless_check_pass.h", "source/opt/instruction.cpp", "source/opt/instruction.h", "source/opt/instruction_list.cpp", "source/opt/instruction_list.h", + "source/opt/instrument_pass.cpp", + "source/opt/instrument_pass.h", "source/opt/ir_builder.h", "source/opt/ir_context.cpp", "source/opt/ir_context.h",
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5872f92..8143123 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -239,6 +239,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/libspirv.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/optimizer.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/linker.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/instrument.hpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/spirv-tools/) endif(ENABLE_SPIRV_TOOLS_INSTALL)
diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp new file mode 100644 index 0000000..69d1ad2 --- /dev/null +++ b/include/spirv-tools/instrument.hpp
@@ -0,0 +1,135 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 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 INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_ +#define INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_ + +// Shader Instrumentation Interface +// +// This file provides an external interface for applications that wish to +// communicate with shaders instrumented by passes created by: +// +// CreateInstBindlessCheckPass +// +// More detailed documentation of this routine can be found in optimizer.hpp + +namespace spvtools { + +// Stream Output Buffer Offsets +// +// The following values provide 32-bit word offsets into the output buffer +// generated by InstrumentPass::GenDebugStreamWrite. This method is utilized +// by InstBindlessCheckPass. +// +// The first word of the debug output buffer contains the next available word +// in the data stream to be written. Shaders will atomically read and update +// this value so as not to overwrite each others records. This value must be +// initialized to zero +static const int kDebugOutputSizeOffset = 0; + +// The second word of the output buffer is the start of the stream of records +// written by the instrumented shaders. Each record represents a validation +// error. The format of the records is documented below. +static const int kDebugOutputDataOffset = 1; + +// Common Stream Record Offsets +// +// The following are offsets to fields which are common to all records written +// to the output stream. +// +// Each record first contains the size of the record in 32-bit words, including +// the size word. +static const int kInstCommonOutSize = 0; + +// This is the shader id passed by the layer when the instrumentation pass is +// created. +static const int kInstCommonOutShaderId = 1; + +// This is the ordinal position of the instruction within the SPIR-V shader +// which generated the validation error. +static const int kInstCommonOutInstructionIdx = 2; + +// This is the stage which generated the validation error. This word is used +// to determine the contents of the next two words in the record. +// 0:Vert, 1:TessCtrl, 2:TessEval, 3:Geom, 4:Frag, 5:Compute +static const int kInstCommonOutStageIdx = 3; +static const int kInstCommonOutCnt = 4; + +// Stage-specific Stream Record Offsets +// +// Each stage will contain different values in the next two words of the record +// used to identify which instantiation of the shader generated the validation +// error. +// +// Vertex Shader Output Record Offsets +static const int kInstVertOutVertexId = kInstCommonOutCnt; +static const int kInstVertOutInstanceId = kInstCommonOutCnt + 1; + +// Frag Shader Output Record Offsets +static const int kInstFragOutFragCoordX = kInstCommonOutCnt; +static const int kInstFragOutFragCoordY = kInstCommonOutCnt + 1; + +// Compute Shader Output Record Offsets +static const int kInstCompOutGlobalInvocationId = kInstCommonOutCnt; +static const int kInstCompOutUnused = kInstCommonOutCnt + 1; + +// Tessellation Shader Output Record Offsets +static const int kInstTessOutInvocationId = kInstCommonOutCnt; +static const int kInstTessOutUnused = kInstCommonOutCnt + 1; + +// Geometry Shader Output Record Offsets +static const int kInstGeomOutPrimitiveId = kInstCommonOutCnt; +static const int kInstGeomOutInvocationId = kInstCommonOutCnt + 1; + +// Size of Common and Stage-specific Members +static const int kInstStageOutCnt = kInstCommonOutCnt + 2; + +// Validation Error Code +// +// This identifies the validation error. It also helps to identify +// how many words follow in the record and their meaning. +static const int kInstValidationOutError = kInstStageOutCnt; + +// Validation-specific Output Record Offsets +// +// Each different validation will generate a potentially different +// number of words at the end of the record giving more specifics +// about the validation error. +// +// A bindless bounds error will output the index and the bound. +static const int kInstBindlessOutDescIndex = kInstStageOutCnt + 1; +static const int kInstBindlessOutDescBound = kInstStageOutCnt + 2; +static const int kInstBindlessOutCnt = kInstStageOutCnt + 3; + +// Maximum Output Record Member Count +static const int kInstMaxOutCnt = kInstStageOutCnt + 3; + +// Validation Error Codes +// +// These are the possible validation error codes. +static const int kInstErrorBindlessBounds = 0; + +// Debug Buffer Bindings +// +// 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. +static const int kDebugOutputBindingStream = 0; + +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 5a3c52f..6f08efd 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp
@@ -657,6 +657,32 @@ // them into a single instruction where possible. Optimizer::PassToken CreateCombineAccessChainsPass(); +// Create a pass to instrument bindless descriptor checking +// This pass instruments all bindless references to check that descriptor +// array indices are inbounds. 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 bindless 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. It may also be beneficial to run Simplification +// (ie Constant Propagation), DeadBranchElim and BlockMerge after this pass to +// optimize instrument code involving the testing of compile-time constants. +// 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. +// +// TODO(greg-lunarg): Add support for vk_ext_descriptor_indexing. +Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set, + uint32_t shader_id); + } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index eac956c..3ab2606 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt
@@ -46,8 +46,10 @@ inline_exhaustive_pass.h inline_opaque_pass.h inline_pass.h + inst_bindless_check_pass.h instruction.h instruction_list.h + instrument_pass.h ir_builder.h ir_context.h ir_loader.h @@ -134,8 +136,10 @@ inline_exhaustive_pass.cpp inline_opaque_pass.cpp inline_pass.cpp + inst_bindless_check_pass.cpp instruction.cpp instruction_list.cpp + instrument_pass.cpp ir_context.cpp ir_loader.cpp licm_pass.cpp
diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h index 0971092..ff3a412 100644 --- a/source/opt/basic_block.h +++ b/source/opt/basic_block.h
@@ -71,6 +71,9 @@ // Appends all of block's instructions (except label) to this block inline void AddInstructions(BasicBlock* bp); + // The pointer to the label starting this basic block. + std::unique_ptr<Instruction>& GetLabel() { return label_; } + // The label starting this basic block. Instruction* GetLabelInst() { return label_.get(); } const Instruction* GetLabelInst() const { return label_.get(); }
diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp index 185dcb7..9990661 100644 --- a/source/opt/decoration_manager.cpp +++ b/source/opt/decoration_manager.cpp
@@ -261,6 +261,7 @@ AddDecoration(&inst); } } + void DecorationManager::AddDecoration(Instruction* inst) { switch (inst->opcode()) { case SpvOpDecorate: @@ -289,6 +290,43 @@ } } +void DecorationManager::AddDecoration(SpvOp opcode, + std::vector<Operand> opnds) { + IRContext* ctx = module_->context(); + std::unique_ptr<Instruction> newDecoOp( + new Instruction(ctx, opcode, 0, 0, opnds)); + ctx->AddAnnotationInst(std::move(newDecoOp)); +} + +void DecorationManager::AddDecoration(uint32_t inst_id, uint32_t decoration) { + AddDecoration( + SpvOpDecorate, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}}}); +} + +void DecorationManager::AddDecorationVal(uint32_t inst_id, uint32_t decoration, + uint32_t decoration_value) { + AddDecoration( + SpvOpDecorate, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {decoration_value}}}); +} + +void DecorationManager::AddMemberDecoration(uint32_t inst_id, uint32_t member, + uint32_t decoration, + uint32_t decoration_value) { + AddDecoration( + SpvOpMemberDecorate, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {member}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {decoration_value}}}); +} + template <typename T> std::vector<T> DecorationManager::InternalGetDecorationsFor( uint32_t id, bool include_linkage) {
diff --git a/source/opt/decoration_manager.h b/source/opt/decoration_manager.h index fb9cfb6..2d87f5f 100644 --- a/source/opt/decoration_manager.h +++ b/source/opt/decoration_manager.h
@@ -111,6 +111,20 @@ // Informs the decoration manager of a new decoration that it needs to track. void AddDecoration(Instruction* inst); + // Add decoration with |opcode| and operands |opnds|. + void AddDecoration(SpvOp opcode, const std::vector<Operand> opnds); + + // Add |decoration| of |inst_id| to module. + void AddDecoration(uint32_t inst_id, uint32_t decoration); + + // Add |decoration, decoration_value| of |inst_id| to module. + void AddDecorationVal(uint32_t inst_id, uint32_t decoration, + uint32_t decoration_value); + + // Add |decoration, decoration_value| of |inst_id, member| to module. + void AddMemberDecoration(uint32_t member, uint32_t inst_id, + uint32_t decoration, uint32_t decoration_value); + private: // Analyzes the defs and uses in the given |module| and populates data // structures in this class. Does nothing if |module| is nullptr.
diff --git a/source/opt/def_use_manager.cpp b/source/opt/def_use_manager.cpp index 54f284c..0ec98ca 100644 --- a/source/opt/def_use_manager.cpp +++ b/source/opt/def_use_manager.cpp
@@ -279,6 +279,16 @@ } if (lhs.inst_to_used_ids_ != rhs.inst_to_used_ids_) { + for (auto p : lhs.inst_to_used_ids_) { + if (rhs.inst_to_used_ids_.count(p.first) == 0) { + return false; + } + } + for (auto p : rhs.inst_to_used_ids_) { + if (lhs.inst_to_used_ids_.count(p.first) == 0) { + return false; + } + } return false; } return true;
diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp new file mode 100644 index 0000000..1901f76 --- /dev/null +++ b/source/opt/inst_bindless_check_pass.cpp
@@ -0,0 +1,263 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 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_bindless_check_pass.h" + +namespace { + +// Input Operand Indices +static const int kSpvImageSampleImageIdInIdx = 0; +static const int kSpvSampledImageImageIdInIdx = 0; +static const int kSpvSampledImageSamplerIdInIdx = 1; +static const int kSpvImageSampledImageIdInIdx = 0; +static const int kSpvLoadPtrIdInIdx = 0; +static const int kSpvAccessChainBaseIdInIdx = 0; +static const int kSpvAccessChainIndex0IdInIdx = 1; +static const int kSpvTypePointerTypeIdInIdx = 1; +static const int kSpvTypeArrayLengthIdInIdx = 1; +static const int kSpvConstantValueInIdx = 0; + +} // anonymous namespace + +namespace spvtools { +namespace opt { + +void InstBindlessCheckPass::GenBindlessCheckCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t instruction_idx, + uint32_t stage_idx, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { + // Look for reference through bindless descriptor. If not, return. + std::unique_ptr<BasicBlock> new_blk_ptr; + uint32_t image_id; + switch (ref_inst_itr->opcode()) { + case SpvOp::SpvOpImageSampleImplicitLod: + case SpvOp::SpvOpImageSampleExplicitLod: + case SpvOp::SpvOpImageSampleDrefImplicitLod: + case SpvOp::SpvOpImageSampleDrefExplicitLod: + case SpvOp::SpvOpImageSampleProjImplicitLod: + case SpvOp::SpvOpImageSampleProjExplicitLod: + case SpvOp::SpvOpImageSampleProjDrefImplicitLod: + case SpvOp::SpvOpImageSampleProjDrefExplicitLod: + case SpvOp::SpvOpImageGather: + case SpvOp::SpvOpImageDrefGather: + case SpvOp::SpvOpImageQueryLod: + case SpvOp::SpvOpImageSparseSampleImplicitLod: + case SpvOp::SpvOpImageSparseSampleExplicitLod: + case SpvOp::SpvOpImageSparseSampleDrefImplicitLod: + case SpvOp::SpvOpImageSparseSampleDrefExplicitLod: + case SpvOp::SpvOpImageSparseSampleProjImplicitLod: + case SpvOp::SpvOpImageSparseSampleProjExplicitLod: + case SpvOp::SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOp::SpvOpImageSparseSampleProjDrefExplicitLod: + case SpvOp::SpvOpImageSparseGather: + case SpvOp::SpvOpImageSparseDrefGather: + case SpvOp::SpvOpImageFetch: + case SpvOp::SpvOpImageRead: + case SpvOp::SpvOpImageQueryFormat: + case SpvOp::SpvOpImageQueryOrder: + case SpvOp::SpvOpImageQuerySizeLod: + case SpvOp::SpvOpImageQuerySize: + case SpvOp::SpvOpImageQueryLevels: + case SpvOp::SpvOpImageQuerySamples: + case SpvOp::SpvOpImageSparseFetch: + case SpvOp::SpvOpImageSparseRead: + case SpvOp::SpvOpImageWrite: + image_id = + ref_inst_itr->GetSingleWordInOperand(kSpvImageSampleImageIdInIdx); + break; + default: + return; + } + Instruction* image_inst = get_def_use_mgr()->GetDef(image_id); + uint32_t load_id; + Instruction* load_inst; + if (image_inst->opcode() == SpvOp::SpvOpSampledImage) { + load_id = image_inst->GetSingleWordInOperand(kSpvSampledImageImageIdInIdx); + load_inst = get_def_use_mgr()->GetDef(load_id); + } else if (image_inst->opcode() == SpvOp::SpvOpImage) { + load_id = image_inst->GetSingleWordInOperand(kSpvImageSampledImageIdInIdx); + load_inst = get_def_use_mgr()->GetDef(load_id); + } else { + load_id = image_id; + load_inst = image_inst; + image_id = 0; + } + if (load_inst->opcode() != SpvOp::SpvOpLoad) { + // TODO(greg-lunarg): Handle additional possibilities + return; + } + uint32_t ptr_id = load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx); + Instruction* ptr_inst = get_def_use_mgr()->GetDef(ptr_id); + if (ptr_inst->opcode() != SpvOp::SpvOpAccessChain) return; + if (ptr_inst->NumInOperands() != 2) { + assert(false && "unexpected bindless index number"); + return; + } + uint32_t index_id = + ptr_inst->GetSingleWordInOperand(kSpvAccessChainIndex0IdInIdx); + ptr_id = ptr_inst->GetSingleWordInOperand(kSpvAccessChainBaseIdInIdx); + ptr_inst = get_def_use_mgr()->GetDef(ptr_id); + if (ptr_inst->opcode() != SpvOpVariable) { + assert(false && "unexpected bindless base"); + return; + } + uint32_t var_type_id = ptr_inst->type_id(); + Instruction* var_type_inst = get_def_use_mgr()->GetDef(var_type_id); + uint32_t ptr_type_id = + var_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx); + Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); + // TODO(greg-lunarg): Handle RuntimeArray. Will need to pull length + // out of debug input buffer. + if (ptr_type_inst->opcode() != SpvOpTypeArray) return; + // If index and bound both compile-time constants and index < bound, + // return without changing + uint32_t length_id = + ptr_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx); + Instruction* index_inst = get_def_use_mgr()->GetDef(index_id); + Instruction* length_inst = get_def_use_mgr()->GetDef(length_id); + if (index_inst->opcode() == SpvOpConstant && + length_inst->opcode() == SpvOpConstant && + index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) < + length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx)) + return; + // Generate full runtime bounds test code with true branch + // being full reference and false branch being debug output and zero + // for the referenced value. + MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); + InstructionBuilder builder( + context(), &*new_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBounds); + Instruction* ult_inst = + builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, index_id, length_id); + 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(ult_inst->result_id(), valid_blk_id, + invalid_blk_id, merge_blk_id, + SpvSelectionControlMaskNone); + // Close selection block and gen valid reference block + new_blocks->push_back(std::move(new_blk_ptr)); + new_blk_ptr.reset(new BasicBlock(std::move(valid_label))); + builder.SetInsertPoint(&*new_blk_ptr); + // Clone descriptor load + Instruction* new_load_inst = + builder.AddLoad(load_inst->type_id(), + load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx)); + uint32_t new_load_id = new_load_inst->result_id(); + get_decoration_mgr()->CloneDecorations(load_inst->result_id(), new_load_id); + uint32_t new_image_id = new_load_id; + // Clone Image/SampledImage with new load, if needed + if (image_id != 0) { + if (image_inst->opcode() == SpvOp::SpvOpSampledImage) { + Instruction* new_image_inst = builder.AddBinaryOp( + image_inst->type_id(), SpvOpSampledImage, new_load_id, + image_inst->GetSingleWordInOperand(kSpvSampledImageSamplerIdInIdx)); + new_image_id = new_image_inst->result_id(); + } else { + assert(image_inst->opcode() == SpvOp::SpvOpImage && "expecting OpImage"); + Instruction* new_image_inst = + builder.AddUnaryOp(image_inst->type_id(), SpvOpImage, new_load_id); + new_image_id = new_image_inst->result_id(); + } + get_decoration_mgr()->CloneDecorations(image_id, new_image_id); + } + // Clone original reference using new image code + std::unique_ptr<Instruction> new_ref_inst(ref_inst_itr->Clone(context())); + uint32_t ref_result_id = ref_inst_itr->result_id(); + uint32_t new_ref_id = 0; + if (ref_result_id != 0) { + new_ref_id = TakeNextId(); + new_ref_inst->SetResultId(new_ref_id); + } + new_ref_inst->SetInOperand(kSpvImageSampleImageIdInIdx, {new_image_id}); + // Register new reference and add to new block + builder.AddInstruction(std::move(new_ref_inst)); + if (new_ref_id != 0) + get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id); + // Close valid block and gen invalid block + (void)builder.AddBranch(merge_blk_id); + new_blocks->push_back(std::move(new_blk_ptr)); + new_blk_ptr.reset(new BasicBlock(std::move(invalid_label))); + builder.SetInsertPoint(&*new_blk_ptr); + uint32_t u_index_id = GenUintCastCode(index_id, &builder); + GenDebugStreamWrite(instruction_idx, stage_idx, + {error_id, u_index_id, length_id}, &builder); + // Remember last invalid block id + uint32_t last_invalid_blk_id = new_blk_ptr->GetLabelInst()->result_id(); + // Gen zero for invalid reference + uint32_t ref_type_id = ref_inst_itr->type_id(); + // Close invalid block and gen merge block + (void)builder.AddBranch(merge_blk_id); + new_blocks->push_back(std::move(new_blk_ptr)); + 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 and move in remainder of original block. + if (new_ref_id != 0) { + Instruction* phi_inst = builder.AddPhi( + ref_type_id, {new_ref_id, valid_blk_id, builder.GetNullId(ref_type_id), + last_invalid_blk_id}); + context()->ReplaceAllUsesWith(ref_result_id, phi_inst->result_id()); + } + context()->KillInst(&*ref_inst_itr); + MovePostludeCode(ref_block_itr, &new_blk_ptr); + // Add remainder/merge block to new blocks + new_blocks->push_back(std::move(new_blk_ptr)); +} + +void InstBindlessCheckPass::InitializeInstBindlessCheck() { + // Initialize base class + InitializeInstrument(); + // Look for related extensions + ext_descriptor_indexing_defined_ = false; + for (auto& ei : get_module()->extensions()) { + const char* ext_name = + reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]); + if (strcmp(ext_name, "SPV_EXT_descriptor_indexing") == 0) { + ext_descriptor_indexing_defined_ = true; + break; + } + } +} + +Pass::Status InstBindlessCheckPass::ProcessImpl() { + // Perform instrumentation on each entry point function in module + InstProcessFunction pfn = + [this](BasicBlock::iterator ref_inst_itr, + UptrVectorIterator<BasicBlock> ref_block_itr, + uint32_t instruction_idx, uint32_t stage_idx, + std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { + return GenBindlessCheckCode(ref_inst_itr, ref_block_itr, + instruction_idx, stage_idx, new_blocks); + }; + bool modified = InstProcessEntryPointCallTree(pfn); + // This pass does not update inst->blk info + context()->InvalidateAnalyses(IRContext::kAnalysisInstrToBlockMapping); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Pass::Status InstBindlessCheckPass::Process() { + InitializeInstBindlessCheck(); + return ProcessImpl(); +} + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h new file mode 100644 index 0000000..eaa1dd2 --- /dev/null +++ b/source/opt/inst_bindless_check_pass.h
@@ -0,0 +1,93 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 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_BINDLESS_CHECK_PASS_H_ +#define LIBSPIRV_OPT_INST_BINDLESS_CHECK_PASS_H_ + +#include "instrument_pass.h" + +namespace spvtools { +namespace opt { + +// This class/pass is designed to support the bindless (descriptor indexing) +// GPU-assisted validation layer of +// https://github.com/KhronosGroup/Vulkan-ValidationLayers. Its internal and +// external design may change as the layer evolves. +class InstBindlessCheckPass : public InstrumentPass { + public: + // For test harness only + InstBindlessCheckPass() : InstrumentPass(7, 23, kInstValidationIdBindless) {} + // For all other interfaces + InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id) + : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless) {} + + ~InstBindlessCheckPass() = default; + + // See optimizer.hpp for pass user documentation. + Status Process() override; + + const char* name() const override { return "inst-bindless-check-pass"; } + + private: + // Initialize state for instrumenting bindless checking + void InitializeInstBindlessCheck(); + + // This function does bindless checking instrumentation on a single + // instruction. It is designed to be passed to + // InstrumentPass::InstProcessEntryPointCallTree(), which applies the + // function to each instruction in a module and replaces the instruction + // if warranted. + // + // If |ref_inst_itr| is a bindless reference, return in |new_blocks| the + // result of instrumenting it with validation code within its block at + // |ref_block_itr|. Specifically, generate code to check that the index + // into the descriptor array is in-bounds. If the check passes, execute + // the remainder of the reference, otherwise write a record to the debug + // output buffer stream including |function_idx, instruction_idx, stage_idx| + // and replace 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. + // + // This instrumentation pass utilizes GenDebugStreamWrite() to write its + // error records. The validation-specific part of the error record will + // have the format: + // + // Validation Error Code (=kInstErrorBindlessBounds) + // Descriptor Index + // Descriptor Array Size + // + // The Descriptor Index is the index which has been determined to be + // out-of-bounds. + // + // The Descriptor Array Size is the size of the descriptor array which was + // indexed. + void GenBindlessCheckCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t instruction_idx, + uint32_t stage_idx, std::vector<std::unique_ptr<BasicBlock>>* new_blocks); + + Pass::Status ProcessImpl(); + + // True if VK_EXT_descriptor_indexing is defined + bool ext_descriptor_indexing_defined_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_INST_BINDLESS_CHECK_PASS_H_
diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp new file mode 100644 index 0000000..3291bbb --- /dev/null +++ b/source/opt/instrument_pass.cpp
@@ -0,0 +1,710 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 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 "instrument_pass.h" + +#include "source/cfa.h" + +namespace { + +// Common Parameter Positions +static const int kInstCommonParamInstIdx = 0; +static const int kInstCommonParamCnt = 1; + +// Indices of operands in SPIR-V instructions +static const int kEntryPointExecutionModelInIdx = 0; +static const int kEntryPointFunctionIdInIdx = 1; + +} // anonymous namespace + +namespace spvtools { +namespace opt { + +void InstrumentPass::MovePreludeCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator<BasicBlock> ref_block_itr, + std::unique_ptr<BasicBlock>* new_blk_ptr) { + same_block_pre_.clear(); + same_block_post_.clear(); + // Initialize new block. Reuse label from original block. + new_blk_ptr->reset(new BasicBlock(std::move(ref_block_itr->GetLabel()))); + // Move contents of original ref block up to ref instruction. + for (auto cii = ref_block_itr->begin(); cii != ref_inst_itr; + cii = ref_block_itr->begin()) { + Instruction* inst = &*cii; + inst->RemoveFromList(); + std::unique_ptr<Instruction> mv_ptr(inst); + // Remember same-block ops for possible regeneration. + if (IsSameBlockOp(&*mv_ptr)) { + auto* sb_inst_ptr = mv_ptr.get(); + same_block_pre_[mv_ptr->result_id()] = sb_inst_ptr; + } + (*new_blk_ptr)->AddInstruction(std::move(mv_ptr)); + } +} + +void InstrumentPass::MovePostludeCode( + UptrVectorIterator<BasicBlock> ref_block_itr, + std::unique_ptr<BasicBlock>* new_blk_ptr) { + // new_blk_ptr->reset(new BasicBlock(NewLabel(ref_block_itr->id()))); + // Move contents of original ref block. + for (auto cii = ref_block_itr->begin(); cii != ref_block_itr->end(); + cii = ref_block_itr->begin()) { + Instruction* inst = &*cii; + inst->RemoveFromList(); + std::unique_ptr<Instruction> mv_inst(inst); + // Regenerate any same-block instruction that has not been seen in the + // current block. + if (same_block_pre_.size() > 0) { + CloneSameBlockOps(&mv_inst, &same_block_post_, &same_block_pre_, + new_blk_ptr); + // Remember same-block ops in this block. + if (IsSameBlockOp(&*mv_inst)) { + const uint32_t rid = mv_inst->result_id(); + same_block_post_[rid] = rid; + } + } + (*new_blk_ptr)->AddInstruction(std::move(mv_inst)); + } +} + +std::unique_ptr<Instruction> InstrumentPass::NewLabel(uint32_t label_id) { + std::unique_ptr<Instruction> newLabel( + new Instruction(context(), SpvOpLabel, 0, label_id, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*newLabel); + return newLabel; +} + +uint32_t InstrumentPass::GenUintCastCode(uint32_t val_id, + InstructionBuilder* builder) { + // Cast value to 32-bit unsigned if necessary + if (get_def_use_mgr()->GetDef(val_id)->type_id() == GetUintId()) + return val_id; + return builder->AddUnaryOp(GetUintId(), SpvOpBitcast, val_id)->result_id(); +} + +void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id, + uint32_t field_offset, + uint32_t field_value_id, + InstructionBuilder* builder) { + // Cast value to 32-bit unsigned if necessary + uint32_t val_id = GenUintCastCode(field_value_id, builder); + // Store value + Instruction* data_idx_inst = + builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id, + builder->GetUintConstantId(field_offset)); + uint32_t buf_id = GetOutputBufferId(); + uint32_t buf_uint_ptr_id = GetOutputBufferUintPtrId(); + Instruction* achain_inst = + builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, + builder->GetUintConstantId(kDebugOutputDataOffset), + data_idx_inst->result_id()); + (void)builder->AddBinaryOp(0, SpvOpStore, achain_inst->result_id(), val_id); +} + +void InstrumentPass::GenCommonStreamWriteCode(uint32_t record_sz, + uint32_t inst_id, + uint32_t stage_idx, + uint32_t base_offset_id, + InstructionBuilder* builder) { + // Store record size + GenDebugOutputFieldCode(base_offset_id, kInstCommonOutSize, + builder->GetUintConstantId(record_sz), builder); + // Store Shader Id + GenDebugOutputFieldCode(base_offset_id, kInstCommonOutShaderId, + builder->GetUintConstantId(shader_id_), builder); + // Store Instruction Idx + GenDebugOutputFieldCode(base_offset_id, kInstCommonOutInstructionIdx, inst_id, + builder); + // Store Stage Idx + GenDebugOutputFieldCode(base_offset_id, kInstCommonOutStageIdx, + builder->GetUintConstantId(stage_idx), builder); +} + +void InstrumentPass::GenFragCoordEltDebugOutputCode( + uint32_t base_offset_id, uint32_t uint_frag_coord_id, uint32_t element, + InstructionBuilder* builder) { + Instruction* element_val_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, uint_frag_coord_id, element); + GenDebugOutputFieldCode(base_offset_id, kInstFragOutFragCoordX + element, + element_val_inst->result_id(), builder); +} + +void InstrumentPass::GenBuiltinOutputCode(uint32_t builtin_id, + uint32_t builtin_off, + uint32_t base_offset_id, + InstructionBuilder* builder) { + // Load and store builtin + Instruction* load_inst = + builder->AddUnaryOp(GetUintId(), SpvOpLoad, builtin_id); + GenDebugOutputFieldCode(base_offset_id, builtin_off, load_inst->result_id(), + builder); +} + +void InstrumentPass::GenUintNullOutputCode(uint32_t field_off, + uint32_t base_offset_id, + InstructionBuilder* builder) { + GenDebugOutputFieldCode(base_offset_id, field_off, + builder->GetNullId(GetUintId()), builder); +} + +void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx, + uint32_t base_offset_id, + InstructionBuilder* builder) { + // TODO(greg-lunarg): Add support for all stages + switch (stage_idx) { + case SpvExecutionModelVertex: { + // Load and store VertexId and InstanceId + GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInVertexId), + kInstVertOutVertexId, base_offset_id, builder); + GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInInstanceId), + kInstVertOutInstanceId, base_offset_id, builder); + } break; + case SpvExecutionModelGLCompute: { + // Load and store GlobalInvocationId. Second word is unused; store zero. + GenBuiltinOutputCode( + context()->GetBuiltinVarId(SpvBuiltInGlobalInvocationId), + kInstCompOutGlobalInvocationId, base_offset_id, builder); + GenUintNullOutputCode(kInstCompOutUnused, base_offset_id, builder); + } break; + case SpvExecutionModelGeometry: { + // Load and store PrimitiveId and InvocationId. + GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInPrimitiveId), + kInstGeomOutPrimitiveId, base_offset_id, builder); + GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInInvocationId), + kInstGeomOutInvocationId, base_offset_id, builder); + } break; + case SpvExecutionModelTessellationControl: + case SpvExecutionModelTessellationEvaluation: { + // Load and store InvocationId. Second word is unused; store zero. + GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInInvocationId), + kInstTessOutInvocationId, base_offset_id, builder); + GenUintNullOutputCode(kInstTessOutUnused, base_offset_id, builder); + } break; + case SpvExecutionModelFragment: { + // Load FragCoord and convert to Uint + Instruction* frag_coord_inst = + builder->AddUnaryOp(GetVec4FloatId(), SpvOpLoad, + context()->GetBuiltinVarId(SpvBuiltInFragCoord)); + Instruction* uint_frag_coord_inst = builder->AddUnaryOp( + GetVec4UintId(), SpvOpBitcast, frag_coord_inst->result_id()); + for (uint32_t u = 0; u < 2u; ++u) + GenFragCoordEltDebugOutputCode( + base_offset_id, uint_frag_coord_inst->result_id(), u, builder); + } break; + default: { assert(false && "unsupported stage"); } break; + } +} + +void InstrumentPass::GenDebugStreamWrite( + uint32_t instruction_idx, uint32_t stage_idx, + const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) { + // Call debug output function. Pass func_idx, instruction_idx and + // validation ids as args. + uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size()); + uint32_t output_func_id = GetStreamWriteFunctionId(stage_idx, val_id_cnt); + std::vector<uint32_t> args = {output_func_id, + builder->GetUintConstantId(instruction_idx)}; + (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end()); + (void)builder->AddNaryOp(GetVoidId(), SpvOpFunctionCall, args); +} + +bool InstrumentPass::IsSameBlockOp(const Instruction* inst) const { + return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage; +} + +void InstrumentPass::CloneSameBlockOps( + std::unique_ptr<Instruction>* inst, + std::unordered_map<uint32_t, uint32_t>* same_blk_post, + std::unordered_map<uint32_t, Instruction*>* same_blk_pre, + std::unique_ptr<BasicBlock>* block_ptr) { + (*inst)->ForEachInId( + [&same_blk_post, &same_blk_pre, &block_ptr, this](uint32_t* iid) { + const auto map_itr = (*same_blk_post).find(*iid); + if (map_itr == (*same_blk_post).end()) { + const auto map_itr2 = (*same_blk_pre).find(*iid); + if (map_itr2 != (*same_blk_pre).end()) { + // Clone pre-call same-block ops, map result id. + const Instruction* in_inst = map_itr2->second; + std::unique_ptr<Instruction> sb_inst(in_inst->Clone(context())); + CloneSameBlockOps(&sb_inst, same_blk_post, same_blk_pre, block_ptr); + const uint32_t rid = sb_inst->result_id(); + const uint32_t nid = this->TakeNextId(); + get_decoration_mgr()->CloneDecorations(rid, nid); + sb_inst->SetResultId(nid); + (*same_blk_post)[rid] = nid; + *iid = nid; + (*block_ptr)->AddInstruction(std::move(sb_inst)); + } + } else { + // Reset same-block op operand. + *iid = map_itr->second; + } + }); +} + +void InstrumentPass::UpdateSucceedingPhis( + std::vector<std::unique_ptr<BasicBlock>>& new_blocks) { + const auto first_blk = new_blocks.begin(); + const auto last_blk = new_blocks.end() - 1; + const uint32_t first_id = (*first_blk)->id(); + const uint32_t last_id = (*last_blk)->id(); + const BasicBlock& const_last_block = *last_blk->get(); + const_last_block.ForEachSuccessorLabel( + [&first_id, &last_id, this](const uint32_t succ) { + BasicBlock* sbp = this->id2block_[succ]; + sbp->ForEachPhiInst([&first_id, &last_id, this](Instruction* phi) { + bool changed = false; + phi->ForEachInId([&first_id, &last_id, &changed](uint32_t* id) { + if (*id == first_id) { + *id = last_id; + changed = true; + } + }); + if (changed) get_def_use_mgr()->AnalyzeInstUse(phi); + }); + }); +} + +// Return id for output buffer uint ptr type +uint32_t InstrumentPass::GetOutputBufferUintPtrId() { + if (output_buffer_uint_ptr_id_ == 0) { + output_buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType( + GetUintId(), SpvStorageClassStorageBuffer); + } + return output_buffer_uint_ptr_id_; +} + +uint32_t InstrumentPass::GetOutputBufferBinding() { + switch (validation_id_) { + case kInstValidationIdBindless: + return kDebugOutputBindingStream; + default: + assert(false && "unexpected validation id"); + } + return 0; +} + +// Return id for output buffer +uint32_t InstrumentPass::GetOutputBufferId() { + if (output_buffer_id_ == 0) { + // If not created yet, create one + analysis::DecorationManager* deco_mgr = get_decoration_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::RuntimeArray uint_rarr_ty(reg_uint_ty); + analysis::Type* reg_uint_rarr_ty = + type_mgr->GetRegisteredType(&uint_rarr_ty); + analysis::Struct obuf_ty({reg_uint_ty, reg_uint_rarr_ty}); + analysis::Type* reg_obuf_ty = type_mgr->GetRegisteredType(&obuf_ty); + uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_obuf_ty); + deco_mgr->AddDecoration(obufTyId, SpvDecorationBlock); + deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset, + SpvDecorationOffset, 0); + deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset, + SpvDecorationOffset, 4); + uint32_t obufTyPtrId_ = + type_mgr->FindPointerToType(obufTyId, SpvStorageClassStorageBuffer); + output_buffer_id_ = TakeNextId(); + std::unique_ptr<Instruction> newVarOp(new Instruction( + context(), SpvOpVariable, obufTyPtrId_, output_buffer_id_, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvStorageClassStorageBuffer}}})); + context()->AddGlobalValue(std::move(newVarOp)); + deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationDescriptorSet, + desc_set_); + deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding, + GetOutputBufferBinding()); + // Look for storage buffer extension. If none, create one. + if (!get_feature_mgr()->HasExtension( + kSPV_KHR_storage_buffer_storage_class)) { + const std::string ext_name("SPV_KHR_storage_buffer_storage_class"); + const auto num_chars = ext_name.size(); + // Compute num words, accommodate the terminating null character. + const auto num_words = (num_chars + 1 + 3) / 4; + std::vector<uint32_t> ext_words(num_words, 0u); + std::memcpy(ext_words.data(), ext_name.data(), num_chars); + context()->AddExtension(std::unique_ptr<Instruction>( + new Instruction(context(), SpvOpExtension, 0u, 0u, + {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); + } + } + return output_buffer_id_; +} + +uint32_t InstrumentPass::GetVec4FloatId() { + if (v4float_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + analysis::Vector v4float_ty(reg_float_ty, 4); + analysis::Type* reg_v4float_ty = type_mgr->GetRegisteredType(&v4float_ty); + v4float_id_ = type_mgr->GetTypeInstruction(reg_v4float_ty); + } + return v4float_id_; +} + +uint32_t InstrumentPass::GetUintId() { + if (uint_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + uint_id_ = type_mgr->GetTypeInstruction(reg_uint_ty); + } + return uint_id_; +} + +uint32_t InstrumentPass::GetVec4UintId() { + if (v4uint_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::Vector v4uint_ty(reg_uint_ty, 4); + analysis::Type* reg_v4uint_ty = type_mgr->GetRegisteredType(&v4uint_ty); + v4uint_id_ = type_mgr->GetTypeInstruction(reg_v4uint_ty); + } + return v4uint_id_; +} + +uint32_t InstrumentPass::GetBoolId() { + if (bool_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Bool bool_ty; + analysis::Type* reg_bool_ty = type_mgr->GetRegisteredType(&bool_ty); + bool_id_ = type_mgr->GetTypeInstruction(reg_bool_ty); + } + return bool_id_; +} + +uint32_t InstrumentPass::GetVoidId() { + if (void_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Void void_ty; + analysis::Type* reg_void_ty = type_mgr->GetRegisteredType(&void_ty); + void_id_ = type_mgr->GetTypeInstruction(reg_void_ty); + } + return void_id_; +} + +uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, + uint32_t val_spec_param_cnt) { + // Total param count is common params plus validation-specific + // params + uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt; + if (output_func_id_ == 0) { + // Create function + output_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(GetVoidId()), param_types); + analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); + std::unique_ptr<Instruction> func_inst(new Instruction( + get_module()->context(), SpvOpFunction, GetVoidId(), output_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> output_func = + MakeUnique<Function>(std::move(func_inst)); + // Add parameters + std::vector<uint32_t> param_vec; + for (uint32_t c = 0; c < param_cnt; ++c) { + uint32_t pid = TakeNextId(); + param_vec.push_back(pid); + std::unique_ptr<Instruction> param_inst( + new Instruction(get_module()->context(), SpvOpFunctionParameter, + GetUintId(), pid, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); + output_func->AddParameter(std::move(param_inst)); + } + // Create first block + uint32_t test_blk_id = TakeNextId(); + std::unique_ptr<Instruction> test_label(NewLabel(test_blk_id)); + std::unique_ptr<BasicBlock> new_blk_ptr = + MakeUnique<BasicBlock>(std::move(test_label)); + InstructionBuilder builder( + context(), &*new_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Gen test if debug output buffer size will not be exceeded. + uint32_t obuf_record_sz = kInstStageOutCnt + val_spec_param_cnt; + uint32_t buf_id = GetOutputBufferId(); + uint32_t buf_uint_ptr_id = GetOutputBufferUintPtrId(); + Instruction* obuf_curr_sz_ac_inst = + builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, + builder.GetUintConstantId(kDebugOutputSizeOffset)); + // Fetch the current debug buffer written size atomically, adding the + // size of the record to be written. + uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz); + uint32_t mask_none_id = builder.GetUintConstantId(SpvMemoryAccessMaskNone); + uint32_t scope_invok_id = builder.GetUintConstantId(SpvScopeInvocation); + Instruction* obuf_curr_sz_inst = builder.AddQuadOp( + GetUintId(), SpvOpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(), + scope_invok_id, mask_none_id, obuf_record_sz_id); + uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id(); + // Compute new written size + Instruction* obuf_new_sz_inst = + builder.AddBinaryOp(GetUintId(), SpvOpIAdd, obuf_curr_sz_id, + builder.GetUintConstantId(obuf_record_sz)); + // Fetch the data bound + Instruction* obuf_bnd_inst = + builder.AddIdLiteralOp(GetUintId(), SpvOpArrayLength, + GetOutputBufferId(), kDebugOutputDataOffset); + // Test that new written size is less than or equal to debug output + // data bound + Instruction* obuf_safe_inst = builder.AddBinaryOp( + GetBoolId(), SpvOpULessThanEqual, obuf_new_sz_inst->result_id(), + obuf_bnd_inst->result_id()); + uint32_t merge_blk_id = TakeNextId(); + uint32_t write_blk_id = TakeNextId(); + std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id)); + std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id)); + (void)builder.AddConditionalBranch(obuf_safe_inst->result_id(), + write_blk_id, merge_blk_id, merge_blk_id, + SpvSelectionControlMaskNone); + // Close safety test block and gen write block + new_blk_ptr->SetParent(&*output_func); + output_func->AddBasicBlock(std::move(new_blk_ptr)); + new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label)); + builder.SetInsertPoint(&*new_blk_ptr); + // Generate common and stage-specific debug record members + GenCommonStreamWriteCode(obuf_record_sz, param_vec[kInstCommonParamInstIdx], + stage_idx, obuf_curr_sz_id, &builder); + GenStageStreamWriteCode(stage_idx, obuf_curr_sz_id, &builder); + // Gen writes of validation specific data + for (uint32_t i = 0; i < val_spec_param_cnt; ++i) { + GenDebugOutputFieldCode(obuf_curr_sz_id, kInstStageOutCnt + i, + param_vec[kInstCommonParamCnt + i], &builder); + } + // Close write block and gen merge block + (void)builder.AddBranch(merge_blk_id); + new_blk_ptr->SetParent(&*output_func); + output_func->AddBasicBlock(std::move(new_blk_ptr)); + new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); + builder.SetInsertPoint(&*new_blk_ptr); + // Close merge block and function and add function to module + (void)builder.AddNullaryOp(0, SpvOpReturn); + new_blk_ptr->SetParent(&*output_func); + output_func->AddBasicBlock(std::move(new_blk_ptr)); + std::unique_ptr<Instruction> func_end_inst( + new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); + output_func->SetFunctionEnd(std::move(func_end_inst)); + context()->AddFunction(std::move(output_func)); + output_func_param_cnt_ = param_cnt; + } + assert(param_cnt == output_func_param_cnt_ && "bad arg count"); + return output_func_id_; +} + +bool InstrumentPass::InstrumentFunction(Function* func, uint32_t stage_idx, + InstProcessFunction& pfn) { + bool modified = false; + // Compute function index + uint32_t function_idx = 0; + for (auto fii = get_module()->begin(); fii != get_module()->end(); ++fii) { + if (&*fii == func) break; + ++function_idx; + } + std::vector<std::unique_ptr<BasicBlock>> new_blks; + // Start count after function instruction + uint32_t instruction_idx = funcIdx2offset_[function_idx] + 1; + // Using block iterators here because of block erasures and insertions. + for (auto bi = func->begin(); bi != func->end(); ++bi) { + // Count block's label + ++instruction_idx; + for (auto ii = bi->begin(); ii != bi->end(); ++instruction_idx) { + // Bump instruction count if debug instructions + instruction_idx += static_cast<uint32_t>(ii->dbg_line_insts().size()); + // Generate instrumentation if warranted + pfn(ii, bi, instruction_idx, stage_idx, &new_blks); + if (new_blks.size() == 0) { + ++ii; + continue; + } + // If there are new blocks we know there will always be two or + // more, so update succeeding phis with label of new last block. + size_t newBlocksSize = new_blks.size(); + assert(newBlocksSize > 1); + UpdateSucceedingPhis(new_blks); + // Replace original block with new block(s) + bi = bi.Erase(); + for (auto& bb : new_blks) { + bb->SetParent(func); + } + bi = bi.InsertBefore(&new_blks); + // Reset block iterator to last new block + for (size_t i = 0; i < newBlocksSize - 1; i++) ++bi; + modified = true; + // Restart instrumenting at beginning of last new block, + // but skip over any new phi or copy instruction. + ii = bi->begin(); + if (ii->opcode() == SpvOpPhi || ii->opcode() == SpvOpCopyObject) ++ii; + new_blks.clear(); + } + } + return modified; +} + +bool InstrumentPass::InstProcessCallTreeFromRoots(InstProcessFunction& pfn, + std::queue<uint32_t>* roots, + uint32_t stage_idx) { + bool modified = false; + std::unordered_set<uint32_t> done; + // Process all functions from roots + while (!roots->empty()) { + const uint32_t fi = roots->front(); + roots->pop(); + if (done.insert(fi).second) { + Function* fn = id2function_.at(fi); + // Add calls first so we don't add new output function + AddCalls(fn, roots); + modified = InstrumentFunction(fn, stage_idx, pfn) || modified; + } + } + return modified; +} + +bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { + // Make sure all entry points have the same execution model. Do not + // instrument if they do not. + // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module + // can contain entry points with different execution models, although + // such modules will likely be rare as GLSL and HLSL are geared toward + // one model per module. In such cases we will need + // to clone any functions which are in the call trees of entrypoints + // with differing execution models. + uint32_t ecnt = 0; + uint32_t stage = SpvExecutionModelMax; + for (auto& e : get_module()->entry_points()) { + if (ecnt == 0) + stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx); + else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != stage) + return false; + ++ecnt; + } + // Only supporting vertex, fragment and compute shaders at the moment. + // TODO(greg-lunarg): Handle all stages. + if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment && + stage != SpvExecutionModelGeometry && + stage != SpvExecutionModelGLCompute && + stage != SpvExecutionModelTessellationControl && + stage != SpvExecutionModelTessellationEvaluation) + return false; + // Add together the roots of all entry points + std::queue<uint32_t> roots; + for (auto& e : get_module()->entry_points()) { + roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); + } + bool modified = InstProcessCallTreeFromRoots(pfn, &roots, stage); + return modified; +} + +void InstrumentPass::InitializeInstrument() { + output_buffer_id_ = 0; + output_buffer_uint_ptr_id_ = 0; + output_func_id_ = 0; + output_func_param_cnt_ = 0; + v4float_id_ = 0; + uint_id_ = 0; + v4uint_id_ = 0; + bool_id_ = 0; + void_id_ = 0; + + // clear collections + id2function_.clear(); + id2block_.clear(); + + // Initialize function and block maps. + for (auto& fn : *get_module()) { + id2function_[fn.result_id()] = &fn; + for (auto& blk : fn) { + id2block_[blk.id()] = &blk; + } + } + + // Calculate instruction offset of first function + uint32_t pre_func_size = 0; + Module* module = get_module(); + for (auto& i : context()->capabilities()) { + (void)i; + ++pre_func_size; + } + for (auto& i : module->extensions()) { + (void)i; + ++pre_func_size; + } + for (auto& i : module->ext_inst_imports()) { + (void)i; + ++pre_func_size; + } + ++pre_func_size; // memory_model + for (auto& i : module->entry_points()) { + (void)i; + ++pre_func_size; + } + for (auto& i : module->execution_modes()) { + (void)i; + ++pre_func_size; + } + for (auto& i : module->debugs1()) { + (void)i; + ++pre_func_size; + } + for (auto& i : module->debugs2()) { + (void)i; + ++pre_func_size; + } + for (auto& i : module->debugs3()) { + (void)i; + ++pre_func_size; + } + for (auto& i : module->annotations()) { + (void)i; + ++pre_func_size; + } + for (auto& i : module->types_values()) { + pre_func_size += 1; + pre_func_size += static_cast<uint32_t>(i.dbg_line_insts().size()); + } + funcIdx2offset_[0] = pre_func_size; + + // Set instruction offsets for all other functions. + uint32_t func_idx = 1; + auto prev_fn = get_module()->begin(); + auto curr_fn = prev_fn; + for (++curr_fn; curr_fn != get_module()->end(); ++curr_fn) { + // Count function and end instructions + uint32_t func_size = 2; + for (auto& blk : *prev_fn) { + // Count label + func_size += 1; + for (auto& inst : blk) { + func_size += 1; + func_size += static_cast<uint32_t>(inst.dbg_line_insts().size()); + } + } + funcIdx2offset_[func_idx] = func_size; + ++prev_fn; + ++func_idx; + } +} + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h new file mode 100644 index 0000000..476a894 --- /dev/null +++ b/source/opt/instrument_pass.h
@@ -0,0 +1,356 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 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_INSTRUMENT_PASS_H_ +#define LIBSPIRV_OPT_INSTRUMENT_PASS_H_ + +#include <list> +#include <memory> +#include <vector> + +#include "source/opt/ir_builder.h" +#include "source/opt/pass.h" +#include "spirv-tools/instrument.hpp" + +// This is a base class to assist in the creation of passes which instrument +// shader modules. More specifically, passes which replace instructions with a +// larger and more capable set of instructions. Commonly, these new +// instructions will add testing of operands and execute different +// instructions depending on the outcome, including outputting of debug +// information into a buffer created especially for that purpose. +// +// This class contains helper functions to create an InstProcessFunction, +// which is the heart of any derived class implementing a specific +// instrumentation pass. It takes an instruction as an argument, decides +// if it should be instrumented, and generates code to replace it. This class +// also supplies function InstProcessEntryPointCallTree which applies the +// InstProcessFunction to every reachable instruction in a module and replaces +// the instruction with new instructions if generated. +// +// Chief among the helper functions are output code generation functions, +// used to generate code in the shader which writes data to output buffers +// associated with that validation. Currently one such function, +// GenDebugStreamWrite, exists. Other such functions may be added in the +// future. Each is accompanied by documentation describing the format of +// its output buffer. +// +// A validation pass may read or write multiple buffers. All such buffers +// are located in a single debug descriptor set whose index is passed at the +// creation of the instrumentation pass. The bindings of the buffers used by +// a validation pass are permanantly assigned and fixed and documented by +// the kDebugOutput* static consts. + +namespace spvtools { +namespace opt { + +// Validation Ids +// These are used to identify the general validation being done and map to +// its output buffers. +static const uint32_t kInstValidationIdBindless = 0; + +class InstrumentPass : public Pass { + using cbb_ptr = const BasicBlock*; + + public: + using InstProcessFunction = std::function<void( + BasicBlock::iterator, UptrVectorIterator<BasicBlock>, uint32_t, uint32_t, + std::vector<std::unique_ptr<BasicBlock>>*)>; + + virtual ~InstrumentPass() = default; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId; + } + + protected: + // Create instrumentation pass which utilizes descriptor set |desc_set| + // for debug input and output buffers and writes |shader_id| into debug + // output records. + InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id) + : Pass(), + desc_set_(desc_set), + shader_id_(shader_id), + validation_id_(validation_id) {} + + // Initialize state for instrumentation of module by |validation_id|. + void InitializeInstrument(); + + // Call |pfn| on all instructions in all functions in the call tree of the + // entry points in |module|. If code is generated for an instruction, replace + // the instruction's block with the new blocks that are generated. Continue + // processing at the top of the last new block. + bool InstProcessEntryPointCallTree(InstProcessFunction& pfn); + + // Move all code in |ref_block_itr| preceding the instruction |ref_inst_itr| + // to be instrumented into block |new_blk_ptr|. + void MovePreludeCode(BasicBlock::iterator ref_inst_itr, + UptrVectorIterator<BasicBlock> ref_block_itr, + std::unique_ptr<BasicBlock>* new_blk_ptr); + + // Move all code in |ref_block_itr| succeeding the instruction |ref_inst_itr| + // to be instrumented into block |new_blk_ptr|. + void MovePostludeCode(UptrVectorIterator<BasicBlock> ref_block_itr, + std::unique_ptr<BasicBlock>* new_blk_ptr); + + // Generate instructions in |builder| which will atomically fetch and + // increment the size of the debug output buffer stream of the current + // validation and write a record to the end of the stream, if enough space + // in the buffer remains. The record will contain the index of the function + // and instruction within that function |func_idx, instruction_idx| which + // generated the record. It will also contain additional information to + // identify the instance of the shader, depending on the stage |stage_idx| + // of the shader. Finally, the record will contain validation-specific + // data contained in |validation_ids| which will identify the validation + // error as well as the values involved in the error. + // + // The output buffer binding written to by the code generated by the function + // is determined by the validation id specified when each specific + // instrumentation pass is created. + // + // The output buffer is a sequence of 32-bit values with the following + // format (where all elements are unsigned 32-bit unless otherwise noted): + // + // Size + // Record0 + // Record1 + // Record2 + // ... + // + // Size is the number of 32-bit values that have been written or + // attempted to be written to the output buffer, excluding the Size. It is + // initialized to 0. If the size of attempts to write the buffer exceeds + // the actual size of the buffer, it is possible that this field can exceed + // the actual size of the buffer. + // + // Each Record* is a variable-length sequence of 32-bit values with the + // following format defined using static const offsets in the .cpp file: + // + // Record Size + // Shader ID + // Instruction Index + // Stage + // Stage-specific Word 0 + // Stage-specific Word 1 + // Validation Error Code + // Validation-specific Word 0 + // Validation-specific Word 1 + // Validation-specific Word 2 + // ... + // + // Each record consists of three subsections: members common across all + // validation, members specific to the stage, and members specific to a + // validation. + // + // The Record Size is the number of 32-bit words in the record, including + // the Record Size word. + // + // Shader ID is a value that identifies which shader has generated the + // validation error. It is passed when the instrumentation pass is created. + // + // The Instruction Index is the position of the instruction within the + // SPIR-V file which is in error. + // + // The Stage is the pipeline stage which has generated the error as defined + // by the SpvExecutionModel_ enumeration. This is used to interpret the + // following Stage-specific words. + // + // The Stage-specific Words identify which invocation of the shader generated + // the error. Every stage will write two words, although in some cases the + // second word is unused and so zero is written. Vertex shaders will write + // the Vertex and Instance ID. Fragment shaders will write FragCoord.xy. + // Compute shaders will write the Global Invocation ID and zero (unused). + // Both tesselation shaders will write the Invocation Id and zero (unused). + // The geometry shader will write the Primitive ID and Invocation ID. + // + // The Validation Error Code specifies the exact error which has occurred. + // These are enumerated with the kInstError* static consts. This allows + // multiple validation layers to use the same, single output buffer. + // + // The Validation-specific Words are a validation-specific number of 32-bit + // words which give further information on the validation error that + // occurred. These are documented further in each file containing the + // validation-specific class which derives from this base class. + // + // Because the code that is generated checks against the size of the buffer + // before writing, the size of the debug out buffer can be used by the + // validation layer to control the number of error records that are written. + void GenDebugStreamWrite(uint32_t instruction_idx, uint32_t stage_idx, + const std::vector<uint32_t>& validation_ids, + InstructionBuilder* builder); + + // Generate code to cast |value_id| to unsigned, if needed. Return + // an id to the unsigned equivalent. + uint32_t GenUintCastCode(uint32_t value_id, InstructionBuilder* builder); + + // Return new label. + std::unique_ptr<Instruction> NewLabel(uint32_t label_id); + + // Return id for 32-bit unsigned type + uint32_t GetUintId(); + + // Return id for 32-bit unsigned type + uint32_t GetBoolId(); + + // Return id for void type + uint32_t GetVoidId(); + + // Return id for output buffer uint type + uint32_t GetOutputBufferUintPtrId(); + + // Return binding for output buffer for current validation. + uint32_t GetOutputBufferBinding(); + + // Return id for debug output buffer + uint32_t GetOutputBufferId(); + + // Return id for v4float type + uint32_t GetVec4FloatId(); + + // Return id for v4uint type + uint32_t GetVec4UintId(); + + // Return id for output function. Define if it doesn't exist with + // |val_spec_arg_cnt| validation-specific uint32 arguments. + uint32_t GetStreamWriteFunctionId(uint32_t stage_idx, + uint32_t val_spec_param_cnt); + + // Apply instrumentation function |pfn| to every instruction in |func|. + // If code is generated for an instruction, replace the instruction's + // block with the new blocks that are generated. Continue processing at the + // top of the last new block. + bool InstrumentFunction(Function* func, uint32_t stage_idx, + InstProcessFunction& pfn); + + // Call |pfn| on all functions in the call tree of the function + // ids in |roots|. + bool InstProcessCallTreeFromRoots(InstProcessFunction& pfn, + std::queue<uint32_t>* roots, + uint32_t stage_idx); + + // Gen code into |builder| to write |field_value_id| into debug output + // buffer at |base_offset_id| + |field_offset|. + void GenDebugOutputFieldCode(uint32_t base_offset_id, uint32_t field_offset, + uint32_t field_value_id, + InstructionBuilder* builder); + + // Generate instructions into |builder| which will write the members + // of the debug output record common for all stages and validations at + // |base_off|. + void GenCommonStreamWriteCode(uint32_t record_sz, uint32_t instruction_idx, + uint32_t stage_idx, uint32_t base_off, + InstructionBuilder* builder); + + // Generate instructions into |builder| which will write + // |uint_frag_coord_id| at |component| of the record at |base_offset_id| of + // the debug output buffer . + void GenFragCoordEltDebugOutputCode(uint32_t base_offset_id, + uint32_t uint_frag_coord_id, + uint32_t component, + InstructionBuilder* builder); + + // Generate instructions into |builder| which will load the uint |builtin_id| + // and write it into the debug output buffer at |base_off| + |builtin_off|. + void GenBuiltinOutputCode(uint32_t builtin_id, uint32_t builtin_off, + uint32_t base_off, InstructionBuilder* builder); + + // Generate instructions into |builder| which will write a uint null into + // the debug output buffer at |base_off| + |builtin_off|. + void GenUintNullOutputCode(uint32_t field_off, uint32_t base_off, + InstructionBuilder* builder); + + // Generate instructions into |builder| which will write the |stage_idx|- + // specific members of the debug output stream at |base_off|. + void GenStageStreamWriteCode(uint32_t stage_idx, uint32_t base_off, + InstructionBuilder* builder); + + // Return true if instruction must be in the same block that its result + // is used. + bool IsSameBlockOp(const Instruction* inst) const; + + // Clone operands which must be in same block as consumer instructions. + // Look in same_blk_pre for instructions that need cloning. Look in + // same_blk_post for instructions already cloned. Add cloned instruction + // to same_blk_post. + void CloneSameBlockOps( + std::unique_ptr<Instruction>* inst, + std::unordered_map<uint32_t, uint32_t>* same_blk_post, + std::unordered_map<uint32_t, Instruction*>* same_blk_pre, + std::unique_ptr<BasicBlock>* block_ptr); + + // Update phis in succeeding blocks to point to new last block + void UpdateSucceedingPhis( + std::vector<std::unique_ptr<BasicBlock>>& new_blocks); + + // Debug descriptor set index + uint32_t desc_set_; + + // Shader module ID written into output record + uint32_t shader_id_; + + // Map from function id to function pointer. + std::unordered_map<uint32_t, Function*> id2function_; + + // Map from block's label id to block. TODO(dnovillo): This is superfluous wrt + // CFG. It has functionality not present in CFG. Consolidate. + std::unordered_map<uint32_t, BasicBlock*> id2block_; + + // Map from function's position index to the offset of its first instruction + std::unordered_map<uint32_t, uint32_t> funcIdx2offset_; + + // result id for OpConstantFalse + uint32_t validation_id_; + + // id for output buffer variable + uint32_t output_buffer_id_; + + // type id for output buffer element + uint32_t output_buffer_uint_ptr_id_; + + // id for debug output function + uint32_t output_func_id_; + + // param count for output function + uint32_t output_func_param_cnt_; + + // id for v4float type + uint32_t v4float_id_; + + // id for v4float type + uint32_t v4uint_id_; + + // id for 32-bit unsigned type + uint32_t uint_id_; + + // id for bool type + uint32_t bool_id_; + + // id for void type + uint32_t void_id_; + + // Pre-instrumentation same-block insts + std::unordered_map<uint32_t, Instruction*> same_block_pre_; + + // Post-instrumentation same-block op ids + std::unordered_map<uint32_t, uint32_t> same_block_post_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_INSTRUMENT_PASS_H_
diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h index a07b255..434c380 100644 --- a/source/opt/ir_builder.h +++ b/source/opt/ir_builder.h
@@ -58,6 +58,78 @@ : InstructionBuilder(context, parent_block, parent_block->end(), preserved_analyses) {} + Instruction* AddNullaryOp(uint32_t type_id, SpvOp opcode) { + std::unique_ptr<Instruction> newUnOp(new Instruction( + GetContext(), opcode, type_id, + opcode == SpvOpReturn ? 0 : GetContext()->TakeNextId(), {})); + return AddInstruction(std::move(newUnOp)); + } + + Instruction* AddUnaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1) { + std::unique_ptr<Instruction> newUnOp(new Instruction( + GetContext(), opcode, type_id, GetContext()->TakeNextId(), + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}})); + return AddInstruction(std::move(newUnOp)); + } + + Instruction* AddBinaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1, + uint32_t operand2) { + std::unique_ptr<Instruction> newBinOp(new Instruction( + GetContext(), opcode, type_id, + opcode == SpvOpStore ? 0 : GetContext()->TakeNextId(), + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}})); + return AddInstruction(std::move(newBinOp)); + } + + Instruction* AddTernaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1, + uint32_t operand2, uint32_t operand3) { + std::unique_ptr<Instruction> newTernOp(new Instruction( + GetContext(), opcode, type_id, GetContext()->TakeNextId(), + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}})); + return AddInstruction(std::move(newTernOp)); + } + + Instruction* AddQuadOp(uint32_t type_id, SpvOp opcode, uint32_t operand1, + uint32_t operand2, uint32_t operand3, + uint32_t operand4) { + std::unique_ptr<Instruction> newQuadOp(new Instruction( + GetContext(), opcode, type_id, GetContext()->TakeNextId(), + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand4}}})); + return AddInstruction(std::move(newQuadOp)); + } + + Instruction* AddIdLiteralOp(uint32_t type_id, SpvOp opcode, uint32_t operand1, + uint32_t operand2) { + std::unique_ptr<Instruction> newBinOp(new Instruction( + GetContext(), opcode, type_id, GetContext()->TakeNextId(), + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {operand2}}})); + return AddInstruction(std::move(newBinOp)); + } + + // Creates an N-ary instruction of |opcode|. + // |typid| must be the id of the instruction's type. + // |operands| must be a sequence of operand ids. + // Use |result| for the result id if non-zero. + Instruction* AddNaryOp(uint32_t type_id, SpvOp opcode, + const std::vector<uint32_t>& operands, + uint32_t result = 0) { + std::vector<Operand> ops; + for (size_t i = 0; i < operands.size(); i++) { + ops.push_back({SPV_OPERAND_TYPE_ID, {operands[i]}}); + } + std::unique_ptr<Instruction> new_inst(new Instruction( + GetContext(), opcode, type_id, + result != 0 ? result : GetContext()->TakeNextId(), ops)); + return AddInstruction(std::move(new_inst)); + } + // Creates a new selection merge instruction. // The id |merge_id| is the merge basic block id. Instruction* AddSelectionMerge( @@ -167,15 +239,10 @@ // The id |type| must be the id of the phi instruction's type. // The vector |incomings| must be a sequence of pairs of <definition id, // parent id>. - Instruction* AddPhi(uint32_t type, const std::vector<uint32_t>& incomings) { + Instruction* AddPhi(uint32_t type, const std::vector<uint32_t>& incomings, + uint32_t result = 0) { assert(incomings.size() % 2 == 0 && "A sequence of pairs is expected"); - std::vector<Operand> phi_ops; - for (size_t i = 0; i < incomings.size(); i++) { - phi_ops.push_back({SPV_OPERAND_TYPE_ID, {incomings[i]}}); - } - std::unique_ptr<Instruction> phi_inst(new Instruction( - GetContext(), SpvOpPhi, type, GetContext()->TakeNextId(), phi_ops)); - return AddInstruction(std::move(phi_inst)); + return AddNaryOp(type, SpvOpPhi, incomings, result); } // Creates an addition instruction. @@ -249,8 +316,8 @@ // Adds a signed int32 constant to the binary. // The |value| parameter is the constant value to be added. - Instruction* Add32BitSignedIntegerConstant(int32_t value) { - return Add32BitConstantInteger<int32_t>(value, true); + Instruction* GetSintConstant(int32_t value) { + return GetIntConstant<int32_t>(value, true); } // Create a composite construct. @@ -270,8 +337,23 @@ } // Adds an unsigned int32 constant to the binary. // The |value| parameter is the constant value to be added. - Instruction* Add32BitUnsignedIntegerConstant(uint32_t value) { - return Add32BitConstantInteger<uint32_t>(value, false); + Instruction* GetUintConstant(uint32_t value) { + return GetIntConstant<uint32_t>(value, false); + } + + uint32_t GetUintConstantId(uint32_t value) { + Instruction* uint_inst = GetUintConstant(value); + return uint_inst->result_id(); + } + + uint32_t GetNullId(uint32_t type_id) { + analysis::TypeManager* type_mgr = GetContext()->get_type_mgr(); + analysis::ConstantManager* const_mgr = GetContext()->get_constant_mgr(); + const analysis::Type* type = type_mgr->GetType(type_id); + const analysis::Constant* null_const = const_mgr->GetConstant(type, {}); + Instruction* null_inst = + const_mgr->GetDefiningInstruction(null_const, type_id); + return null_inst->result_id(); } // Adds either a signed or unsigned 32 bit integer constant to the binary @@ -279,7 +361,7 @@ // signed constant otherwise as an unsigned constant. If |sign| is false the // value must not be a negative number. template <typename T> - Instruction* Add32BitConstantInteger(T value, bool sign) { + Instruction* GetIntConstant(T value, bool sign) { // Assert that we are not trying to store a negative number in an unsigned // type. if (!sign)
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 4097484..038ad6d 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp
@@ -21,6 +21,15 @@ #include "source/opt/mem_pass.h" #include "source/opt/reflect.h" +namespace { + +static const int kSpvDecorateTargetIdInIdx = 0; +static const int kSpvDecorateDecorationInIdx = 1; +static const int kSpvDecorateBuiltinInIdx = 2; +static const int kEntryPointInterfaceInIdx = 3; + +} // anonymous namespace + namespace spvtools { namespace opt { @@ -43,6 +52,9 @@ if (set & kAnalysisLoopAnalysis) { ResetLoopAnalysis(); } + if (set & kAnalysisBuiltinVarId) { + ResetBuiltinAnalysis(); + } if (set & kAnalysisNameMap) { BuildIdToNameMap(); } @@ -79,6 +91,9 @@ if (analyses_to_invalidate & kAnalysisCombinators) { combinator_ops_.clear(); } + if (analyses_to_invalidate & kAnalysisBuiltinVarId) { + builtin_var_id_map_.clear(); + } if (analyses_to_invalidate & kAnalysisCFG) { cfg_.reset(nullptr); } @@ -573,6 +588,91 @@ return &it->second; } +uint32_t IRContext::FindBuiltinVar(uint32_t builtin) { + for (auto& a : module_->annotations()) { + if (a.opcode() != SpvOpDecorate) continue; + if (a.GetSingleWordInOperand(kSpvDecorateDecorationInIdx) != + SpvDecorationBuiltIn) + continue; + if (a.GetSingleWordInOperand(kSpvDecorateBuiltinInIdx) != builtin) continue; + uint32_t target_id = a.GetSingleWordInOperand(kSpvDecorateTargetIdInIdx); + Instruction* b_var = get_def_use_mgr()->GetDef(target_id); + if (b_var->opcode() != SpvOpVariable) continue; + return target_id; + } + return 0; +} + +void IRContext::AddVarToEntryPoints(uint32_t var_id) { + uint32_t ocnt = 0; + for (auto& e : module()->entry_points()) { + bool found = false; + e.ForEachInOperand([&ocnt, &found, &var_id](const uint32_t* idp) { + if (ocnt >= kEntryPointInterfaceInIdx) { + if (*idp == var_id) found = true; + } + ++ocnt; + }); + if (!found) { + e.AddOperand({SPV_OPERAND_TYPE_ID, {var_id}}); + get_def_use_mgr()->AnalyzeInstDefUse(&e); + } + } +} + +uint32_t IRContext::GetBuiltinVarId(uint32_t builtin) { + if (!AreAnalysesValid(kAnalysisBuiltinVarId)) ResetBuiltinAnalysis(); + // If cached, return it. + std::unordered_map<uint32_t, uint32_t>::iterator it = + builtin_var_id_map_.find(builtin); + if (it != builtin_var_id_map_.end()) return it->second; + // Look for one in shader + uint32_t var_id = FindBuiltinVar(builtin); + if (var_id == 0) { + // If not found, create it + // TODO(greg-lunarg): Add support for all builtins + analysis::TypeManager* type_mgr = get_type_mgr(); + analysis::Type* reg_type; + switch (builtin) { + case SpvBuiltInFragCoord: { + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + analysis::Vector v4float_ty(reg_float_ty, 4); + reg_type = type_mgr->GetRegisteredType(&v4float_ty); + break; + } + case SpvBuiltInVertexId: + case SpvBuiltInInstanceId: + case SpvBuiltInPrimitiveId: + case SpvBuiltInInvocationId: + case SpvBuiltInGlobalInvocationId: { + analysis::Integer uint_ty(32, false); + reg_type = type_mgr->GetRegisteredType(&uint_ty); + break; + } + default: { + assert(false && "unhandled builtin"); + return 0; + } + } + uint32_t type_id = type_mgr->GetTypeInstruction(reg_type); + uint32_t varTyPtrId = + type_mgr->FindPointerToType(type_id, SpvStorageClassInput); + var_id = TakeNextId(); + std::unique_ptr<Instruction> newVarOp( + new Instruction(this, SpvOpVariable, varTyPtrId, var_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvStorageClassInput}}})); + get_def_use_mgr()->AnalyzeInstDefUse(&*newVarOp); + module()->AddGlobalValue(std::move(newVarOp)); + get_decoration_mgr()->AddDecorationVal(var_id, SpvDecorationBuiltIn, + builtin); + AddVarToEntryPoints(var_id); + } + builtin_var_id_map_[builtin] = var_id; + return var_id; +} + // Gets the dominator analysis for function |f|. DominatorAnalysis* IRContext::GetDominatorAnalysis(const Function* f) { if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) {
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 742c868..6e9eda8 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h
@@ -73,7 +73,8 @@ kAnalysisRegisterPressure = 1 << 9, kAnalysisValueNumberTable = 1 << 10, kAnalysisStructuredCFG = 1 << 11, - kAnalysisEnd = 1 << 12 + kAnalysisBuiltinVarId = 1 << 12, + kAnalysisEnd = 1 << 13 }; friend inline Analysis operator|(Analysis lhs, Analysis rhs); @@ -472,6 +473,11 @@ uint32_t max_id_bound() const { return max_id_bound_; } void set_max_id_bound(uint32_t new_bound) { max_id_bound_ = new_bound; } + // Return id of variable only decorated with |builtin|, if in module. + // Create variable and return its id otherwise. If builtin not currently + // supported, return 0. + uint32_t GetBuiltinVarId(uint32_t builtin); + private: // Builds the def-use manager from scratch, even if it was already valid. void BuildDefUseManager() { @@ -543,6 +549,13 @@ valid_analyses_ = valid_analyses_ | kAnalysisLoopAnalysis; } + // Removes all computed loop descriptors. + void ResetBuiltinAnalysis() { + // Clear the cache. + builtin_var_id_map_.clear(); + valid_analyses_ = valid_analyses_ | kAnalysisBuiltinVarId; + } + // Analyzes the features in the owned module. Builds the manager if required. void AnalyzeFeatures() { feature_mgr_ = MakeUnique<FeatureManager>(grammar_); @@ -566,6 +579,13 @@ // true if the cfg is invalidated. bool CheckCFG(); + // Return id of variable only decorated with |builtin|, if in module. + // Return 0 otherwise. + uint32_t FindBuiltinVar(uint32_t builtin); + + // Add |var_id| to all entry points in module. + void AddVarToEntryPoints(uint32_t var_id); + // The SPIR-V syntax context containing grammar tables for opcodes and // operands. spv_context syntax_context_; @@ -607,6 +627,10 @@ // without side-effect. std::unordered_map<uint32_t, std::unordered_set<uint32_t>> combinator_ops_; + // Opcodes of shader capability core executable instructions + // without side-effect. + std::unordered_map<uint32_t, uint32_t> builtin_var_id_map_; + // The CFG for all the functions in |module_|. std::unique_ptr<CFG> cfg_; @@ -786,6 +810,9 @@ } void IRContext::AddExtension(std::unique_ptr<Instruction>&& e) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(e.get()); + } module()->AddExtension(std::move(e)); } @@ -827,6 +854,9 @@ if (AreAnalysesValid(kAnalysisDecorations)) { get_decoration_mgr()->AddDecoration(a.get()); } + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(a.get()); + } module()->AddAnnotationInst(std::move(a)); } @@ -838,10 +868,10 @@ } void IRContext::AddGlobalValue(std::unique_ptr<Instruction>&& v) { - module()->AddGlobalValue(std::move(v)); if (AreAnalysesValid(kAnalysisDefUse)) { - get_def_use_mgr()->AnalyzeInstDef(&*(--types_values_end())); + get_def_use_mgr()->AnalyzeInstDefUse(&*v); } + module()->AddGlobalValue(std::move(v)); } void IRContext::AddFunction(std::unique_ptr<Function>&& f) {
diff --git a/source/opt/loop_peeling.cpp b/source/opt/loop_peeling.cpp index 7d27480..227ba4a 100644 --- a/source/opt/loop_peeling.cpp +++ b/source/opt/loop_peeling.cpp
@@ -151,7 +151,7 @@ context_, &*insert_point, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); Instruction* uint_1_cst = - builder.Add32BitConstantInteger<uint32_t>(1, int_type_->IsSigned()); + builder.GetIntConstant<uint32_t>(1, int_type_->IsSigned()); // Create the increment. // Note that we do "1 + 1" here, one of the operand should the phi // value but we don't have it yet. The operand will be set latter. @@ -162,8 +162,7 @@ canonical_induction_variable_ = builder.AddPhi( uint_1_cst->type_id(), - {builder.Add32BitConstantInteger<uint32_t>(0, int_type_->IsSigned()) - ->result_id(), + {builder.GetIntConstant<uint32_t>(0, int_type_->IsSigned())->result_id(), GetClonedLoop()->GetPreHeaderBlock()->id(), iv_inc->result_id(), GetClonedLoop()->GetLatchBlock()->id()}); // Connect everything. @@ -422,7 +421,7 @@ context_, &*cloned_loop_->GetPreHeaderBlock()->tail(), IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); Instruction* factor = - builder.Add32BitConstantInteger(peel_factor, int_type_->IsSigned()); + builder.GetIntConstant(peel_factor, int_type_->IsSigned()); Instruction* has_remaining_iteration = builder.AddLessThan( factor->result_id(), loop_iteration_count_->result_id()); @@ -484,7 +483,7 @@ context_, &*cloned_loop_->GetPreHeaderBlock()->tail(), IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); Instruction* factor = - builder.Add32BitConstantInteger(peel_factor, int_type_->IsSigned()); + builder.GetIntConstant(peel_factor, int_type_->IsSigned()); Instruction* has_remaining_iteration = builder.AddLessThan( factor->result_id(), loop_iteration_count_->result_id()); @@ -677,8 +676,8 @@ InstructionBuilder( context(), loop->GetHeaderBlock(), IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping) - .Add32BitConstantInteger<uint32_t>(static_cast<uint32_t>(iterations), - is_signed), + .GetIntConstant<uint32_t>(static_cast<uint32_t>(iterations), + is_signed), canonical_induction_variable); if (!peeler.CanPeelLoop()) {
diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp index 96ee94d..d3e733a 100644 --- a/source/opt/loop_unroller.cpp +++ b/source/opt/loop_unroller.cpp
@@ -452,11 +452,9 @@ // If the remainder is negative then we add a signed constant, otherwise just // add an unsigned constant. if (remainder < 0) { - new_constant = - builder.Add32BitSignedIntegerConstant(static_cast<int32_t>(remainder)); + new_constant = builder.GetSintConstant(static_cast<int32_t>(remainder)); } else { - new_constant = builder.Add32BitUnsignedIntegerConstant( - static_cast<int32_t>(remainder)); + new_constant = builder.GetUintConstant(static_cast<int32_t>(remainder)); } uint32_t constant_id = new_constant->result_id();
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 5a1b314..b1bb345 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp
@@ -368,6 +368,12 @@ RegisterPass(CreateWorkaround1209Pass()); } else if (pass_name == "replace-invalid-opcode") { RegisterPass(CreateReplaceInvalidOpcodePass()); + } else if (pass_name == "inst-bindless-check") { + RegisterPass(CreateInstBindlessCheckPass(7, 23)); + RegisterPass(CreateSimplificationPass()); + RegisterPass(CreateDeadBranchElimPass()); + RegisterPass(CreateBlockMergePass()); + RegisterPass(CreateAggressiveDCEPass()); } else if (pass_name == "simplify-instructions") { RegisterPass(CreateSimplificationPass()); } else if (pass_name == "ssa-rewrite") { @@ -747,4 +753,11 @@ return MakeUnique<Optimizer::PassToken::Impl>( MakeUnique<opt::CombineAccessChains>()); } + +Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set, + uint32_t shader_id) { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id)); +} + } // namespace spvtools
diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp index 4c4a232..4bf719d 100644 --- a/source/opt/pass.cpp +++ b/source/opt/pass.cpp
@@ -44,8 +44,9 @@ // Collect all of the entry points as the roots. std::queue<uint32_t> roots; - for (auto& e : module->entry_points()) + for (auto& e : module->entry_points()) { roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); + } return ProcessCallTreeFromRoots(pfn, id2function, &roots); }
diff --git a/source/opt/passes.h b/source/opt/passes.h index 987af1e..25e3a47 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h
@@ -36,6 +36,7 @@ #include "source/opt/if_conversion.h" #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/licm_pass.h" #include "source/opt/local_access_chain_convert_pass.h" #include "source/opt/local_redundancy_elimination.h"
diff --git a/source/opt/types.cpp b/source/opt/types.cpp index 15cff54..0cbe5c8 100644 --- a/source/opt/types.cpp +++ b/source/opt/types.cpp
@@ -555,6 +555,14 @@ } } +Function::Function(Type* ret_type, std::vector<const Type*>& params) + : Type(kFunction), return_type_(ret_type), param_types_(params) { + for (auto* t : params) { + (void)t; + assert(!t->AsVoid()); + } +} + bool Function::IsSameImpl(const Type* that, IsSameCache* seen) const { const Function* ft = that->AsFunction(); if (!ft) return false;
diff --git a/source/opt/types.h b/source/opt/types.h index 625f342..28731c6 100644 --- a/source/opt/types.h +++ b/source/opt/types.h
@@ -491,6 +491,7 @@ 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 Function&) = default; std::string str() const override;
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 240aed1..05957b2 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt
@@ -42,6 +42,7 @@ inline_opaque_test.cpp inline_test.cpp insert_extract_elim_test.cpp + inst_bindless_check_test.cpp instruction_list_test.cpp instruction_test.cpp ir_builder.cpp @@ -86,4 +87,3 @@ LIBS SPIRV-Tools-opt PCH_FILE pch_test_opt ) -
diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp new file mode 100644 index 0000000..1a1a194 --- /dev/null +++ b/test/opt/inst_bindless_check_test.cpp
@@ -0,0 +1,1850 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 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 <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 InstBindlessTest = PassTest<::testing::Test>; + +TEST_F(InstBindlessTest, Simple) { + // Texture2D g_tColor[128]; + // + // layout(push_constant) cbuffer PerViewConstantBuffer_t + // { + // uint g_nDataIdx; + // }; + // + // SamplerState g_sAniso; + // + // struct PS_INPUT + // { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT + // { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT ps_output; + // ps_output.vColor = + // g_tColor[ g_nDataIdx ].Sample(g_sAniso, i.vTextureCoords.xy); + // return ps_output; + // } + + const std::string entry_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +)"; + + const std::string entry_after = + R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +)"; + + const std::string names_annots = + R"(OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +)"; + + const std::string new_annots = + R"(OpDecorate %_struct_55 Block +OpMemberDecorate %_struct_55 0 Offset 0 +OpMemberDecorate %_struct_55 1 Offset 4 +OpDecorate %57 DescriptorSet 7 +OpDecorate %57 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +)"; + + const std::string consts_types_vars = + R"(%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%16 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_16_uint_128 = OpTypeArray %16 %uint_128 +%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 +%24 = OpTypeSampler +%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24 +%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant +%26 = OpTypeSampledImage %16 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string new_consts_types_vars = + R"(%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%48 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_55 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 +%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_9 = OpConstant %uint 9 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_56 = OpConstant %uint 56 +%103 = OpConstantNull %v4float +)"; + + const std::string func_pt1 = + R"(%MainPs = OpFunction %void None %10 +%29 = OpLabel +%30 = OpLoad %v2float %i_vTextureCoords +%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%32 = OpLoad %uint %31 +%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32 +%34 = OpLoad %16 %33 +%35 = OpLoad %24 %g_sAniso +%36 = OpSampledImage %26 %34 %35 +)"; + + const std::string func_pt2_before = + R"(%37 = OpImageSampleImplicitLod %v4float %36 %30 +OpStore %_entryPointOutput_vColor %37 +OpReturn +OpFunctionEnd +)"; + + const std::string func_pt2_after = + R"(%40 = OpULessThan %bool %32 %uint_128 +OpSelectionMerge %41 None +OpBranchConditional %40 %42 %43 +%42 = OpLabel +%44 = OpLoad %16 %33 +%45 = OpSampledImage %26 %44 %35 +%46 = OpImageSampleImplicitLod %v4float %45 %30 +OpBranch %41 +%43 = OpLabel +%102 = OpFunctionCall %void %47 %uint_56 %uint_0 %32 %uint_128 +OpBranch %41 +%41 = OpLabel +%104 = OpPhi %v4float %46 %42 %103 %43 +OpStore %_entryPointOutput_vColor %104 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%47 = OpFunction %void None %48 +%49 = OpFunctionParameter %uint +%50 = OpFunctionParameter %uint +%51 = OpFunctionParameter %uint +%52 = OpFunctionParameter %uint +%53 = OpLabel +%59 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 +%62 = OpAtomicIAdd %uint %59 %uint_4 %uint_0 %uint_9 +%63 = OpIAdd %uint %62 %uint_9 +%64 = OpArrayLength %uint %57 1 +%65 = OpULessThanEqual %bool %63 %64 +OpSelectionMerge %66 None +OpBranchConditional %65 %67 %66 +%67 = OpLabel +%68 = OpIAdd %uint %62 %uint_0 +%70 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %68 +OpStore %70 %uint_9 +%72 = OpIAdd %uint %62 %uint_1 +%73 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %72 +OpStore %73 %uint_23 +%75 = OpIAdd %uint %62 %uint_2 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 +OpStore %76 %49 +%78 = OpIAdd %uint %62 %uint_3 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %78 +OpStore %79 %uint_4 +%82 = OpLoad %v4float %gl_FragCoord +%84 = OpBitcast %v4uint %82 +%85 = OpCompositeExtract %uint %84 0 +%86 = OpIAdd %uint %62 %uint_4 +%87 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %86 +OpStore %87 %85 +%88 = OpCompositeExtract %uint %84 1 +%90 = OpIAdd %uint %62 %uint_5 +%91 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %90 +OpStore %91 %88 +%93 = OpIAdd %uint %62 %uint_6 +%94 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %93 +OpStore %94 %50 +%96 = OpIAdd %uint %62 %uint_7 +%97 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %96 +OpStore %97 %51 +%99 = OpIAdd %uint %62 %uint_8 +%100 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %99 +OpStore %100 %52 +OpBranch %66 +%66 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck<InstBindlessCheckPass>( + entry_before + names_annots + consts_types_vars + func_pt1 + + func_pt2_before, + entry_after + names_annots + new_annots + consts_types_vars + + new_consts_types_vars + func_pt1 + func_pt2_after + output_func, + true, true); +} + +TEST_F(InstBindlessTest, NoInstrumentConstIndexInbounds) { + // Texture2D g_tColor[128]; + // + // SamplerState g_sAniso; + // + // struct PS_INPUT + // { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT + // { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT ps_output; + // + // ps_output.vColor = g_tColor[ 37 ].Sample(g_sAniso, i.vTextureCoords.xy); + // return ps_output; + // } + + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_37 = OpConstant %int 37 +%15 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_15_uint_128 = OpTypeArray %15 %uint_128 +%_ptr_UniformConstant__arr_15_uint_128 = OpTypePointer UniformConstant %_arr_15_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_15_uint_128 UniformConstant +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 +%21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 +%g_sAniso = OpVariable %_ptr_UniformConstant_21 UniformConstant +%23 = OpTypeSampledImage %15 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%MainPs = OpFunction %void None %8 +%26 = OpLabel +%27 = OpLoad %v2float %i_vTextureCoords +%28 = OpAccessChain %_ptr_UniformConstant_15 %g_tColor %int_37 +%29 = OpLoad %15 %28 +%30 = OpLoad %21 %g_sAniso +%31 = OpSampledImage %23 %29 %30 +%32 = OpImageSampleImplicitLod %v4float %31 %27 +OpStore %_entryPointOutput_vColor %32 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck<InstBindlessCheckPass>(before, before, true, true); +} + +TEST_F(InstBindlessTest, InstrumentMultipleInstructions) { + // Texture2D g_tColor[128]; + // + // layout(push_constant) cbuffer PerViewConstantBuffer_t + // { + // uint g_nDataIdx; + // uint g_nDataIdx2; + // }; + // + // SamplerState g_sAniso; + // + // struct PS_INPUT + // { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT + // { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT ps_output; + // + // float t = g_tColor[g_nDataIdx ].Sample(g_sAniso, i.vTextureCoords.xy); + // float t2 = g_tColor[g_nDataIdx2].Sample(g_sAniso, i.vTextureCoords.xy); + // ps_output.vColor = t + t2; + // return ps_output; + // } + + const std::string defs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%17 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_17_uint_128 = OpTypeArray %17 %uint_128 +%_ptr_UniformConstant__arr_17_uint_128 = OpTypePointer UniformConstant %_arr_17_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_17_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%25 = OpTypeSampler +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 +%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant +%27 = OpTypeSampledImage %17 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_struct_63 Block +OpMemberDecorate %_struct_63 0 Offset 0 +OpMemberDecorate %_struct_63 1 Offset 4 +OpDecorate %65 DescriptorSet 7 +OpDecorate %65 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%17 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_17_uint_128 = OpTypeArray %17 %uint_128 +%_ptr_UniformConstant__arr_17_uint_128 = OpTypePointer UniformConstant %_arr_17_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_17_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%25 = OpTypeSampler +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 +%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant +%27 = OpTypeSampledImage %17 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%56 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_63 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 +%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_9 = OpConstant %uint 9 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_58 = OpConstant %uint 58 +%111 = OpConstantNull %v4float +%uint_64 = OpConstant %uint 64 +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %10 +%30 = OpLabel +%31 = OpLoad %v2float %i_vTextureCoords +%32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%33 = OpLoad %uint %32 +%34 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %33 +%35 = OpLoad %17 %34 +%36 = OpLoad %25 %g_sAniso +%37 = OpSampledImage %27 %35 %36 +%38 = OpImageSampleImplicitLod %v4float %37 %31 +%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1 +%40 = OpLoad %uint %39 +%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40 +%42 = OpLoad %17 %41 +%43 = OpSampledImage %27 %42 %36 +%44 = OpImageSampleImplicitLod %v4float %43 %31 +%45 = OpFAdd %v4float %38 %44 +OpStore %_entryPointOutput_vColor %45 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %10 +%30 = OpLabel +%31 = OpLoad %v2float %i_vTextureCoords +%32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%33 = OpLoad %uint %32 +%34 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %33 +%35 = OpLoad %17 %34 +%36 = OpLoad %25 %g_sAniso +%37 = OpSampledImage %27 %35 %36 +%48 = OpULessThan %bool %33 %uint_128 +OpSelectionMerge %49 None +OpBranchConditional %48 %50 %51 +%50 = OpLabel +%52 = OpLoad %17 %34 +%53 = OpSampledImage %27 %52 %36 +%54 = OpImageSampleImplicitLod %v4float %53 %31 +OpBranch %49 +%51 = OpLabel +%110 = OpFunctionCall %void %55 %uint_58 %uint_0 %33 %uint_128 +OpBranch %49 +%49 = OpLabel +%112 = OpPhi %v4float %54 %50 %111 %51 +%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1 +%40 = OpLoad %uint %39 +%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40 +%42 = OpLoad %17 %41 +%43 = OpSampledImage %27 %42 %36 +%113 = OpULessThan %bool %40 %uint_128 +OpSelectionMerge %114 None +OpBranchConditional %113 %115 %116 +%115 = OpLabel +%117 = OpLoad %17 %41 +%118 = OpSampledImage %27 %117 %36 +%119 = OpImageSampleImplicitLod %v4float %118 %31 +OpBranch %114 +%116 = OpLabel +%121 = OpFunctionCall %void %55 %uint_64 %uint_0 %40 %uint_128 +OpBranch %114 +%114 = OpLabel +%122 = OpPhi %v4float %119 %115 %111 %116 +%45 = OpFAdd %v4float %112 %122 +OpStore %_entryPointOutput_vColor %45 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%55 = OpFunction %void None %56 +%57 = OpFunctionParameter %uint +%58 = OpFunctionParameter %uint +%59 = OpFunctionParameter %uint +%60 = OpFunctionParameter %uint +%61 = OpLabel +%67 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 +%70 = OpAtomicIAdd %uint %67 %uint_4 %uint_0 %uint_9 +%71 = OpIAdd %uint %70 %uint_9 +%72 = OpArrayLength %uint %65 1 +%73 = OpULessThanEqual %bool %71 %72 +OpSelectionMerge %74 None +OpBranchConditional %73 %75 %74 +%75 = OpLabel +%76 = OpIAdd %uint %70 %uint_0 +%78 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %76 +OpStore %78 %uint_9 +%80 = OpIAdd %uint %70 %uint_1 +%81 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %80 +OpStore %81 %uint_23 +%83 = OpIAdd %uint %70 %uint_2 +%84 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %83 +OpStore %84 %57 +%86 = OpIAdd %uint %70 %uint_3 +%87 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %86 +OpStore %87 %uint_4 +%90 = OpLoad %v4float %gl_FragCoord +%92 = OpBitcast %v4uint %90 +%93 = OpCompositeExtract %uint %92 0 +%94 = OpIAdd %uint %70 %uint_4 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 +OpStore %95 %93 +%96 = OpCompositeExtract %uint %92 1 +%98 = OpIAdd %uint %70 %uint_5 +%99 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %98 +OpStore %99 %96 +%101 = OpIAdd %uint %70 %uint_6 +%102 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %101 +OpStore %102 %58 +%104 = OpIAdd %uint %70 %uint_7 +%105 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %104 +OpStore %105 %59 +%107 = OpIAdd %uint %70 %uint_8 +%108 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %107 +OpStore %108 %60 +OpBranch %74 +%74 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck<InstBindlessCheckPass>( + defs_before + func_before, defs_after + func_after + output_func, true, + true); +} + +TEST_F(InstBindlessTest, ReuseConstsTypesBuiltins) { + // This test verifies that the pass resuses existing constants, types + // and builtin variables. This test was created by editing the SPIR-V + // from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %85 DescriptorSet 7 +OpDecorate %85 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%20 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_20_uint_128 = OpTypeArray %20 %uint_128 +%_ptr_UniformConstant__arr_20_uint_128 = OpTypePointer UniformConstant %_arr_20_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_20_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 +%35 = OpTypeSampler +%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35 +%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant +%39 = OpTypeSampledImage %20 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_83 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_83 = OpTypePointer StorageBuffer %_struct_83 +%85 = OpVariable %_ptr_StorageBuffer__struct_83 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_9 = OpConstant %uint 9 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%131 = OpConstantNull %v4float +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %10 DescriptorSet 7 +OpDecorate %10 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +OpDecorate %_struct_34 Block +OpMemberDecorate %_struct_34 0 Offset 0 +OpMemberDecorate %_struct_34 1 Offset 4 +OpDecorate %74 DescriptorSet 7 +OpDecorate %74 Binding 0 +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_18_uint_128 = OpTypeArray %18 %uint_128 +%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 +%26 = OpTypeSampler +%_ptr_UniformConstant_26 = OpTypePointer UniformConstant %26 +%g_sAniso = OpVariable %_ptr_UniformConstant_26 UniformConstant +%28 = OpTypeSampledImage %18 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_34 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_34 = OpTypePointer StorageBuffer %_struct_34 +%10 = OpVariable %_ptr_StorageBuffer__struct_34 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_9 = OpConstant %uint 9 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%50 = OpConstantNull %v4float +%68 = OpTypeFunction %void %uint %uint %uint %uint +%74 = OpVariable %_ptr_StorageBuffer__struct_34 StorageBuffer +%uint_82 = OpConstant %uint 82 +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %v2float %i_vTextureCoords +%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64 +%67 = OpLoad %35 %g_sAniso +%78 = OpLoad %20 %65 +%79 = OpSampledImage %39 %78 %67 +%71 = OpImageSampleImplicitLod %v4float %79 %53 +OpStore %_entryPointOutput_vColor %71 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %12 +%51 = OpLabel +%52 = OpLoad %v2float %i_vTextureCoords +%53 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%54 = OpLoad %uint %53 +%55 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %54 +%56 = OpLoad %26 %g_sAniso +%57 = OpLoad %18 %55 +%58 = OpSampledImage %28 %57 %56 +%60 = OpULessThan %bool %54 %uint_128 +OpSelectionMerge %61 None +OpBranchConditional %60 %62 %63 +%62 = OpLabel +%64 = OpLoad %18 %55 +%65 = OpSampledImage %28 %64 %56 +%66 = OpImageSampleImplicitLod %v4float %65 %52 +OpBranch %61 +%63 = OpLabel +%105 = OpFunctionCall %void %67 %uint_82 %uint_0 %54 %uint_128 +OpBranch %61 +%61 = OpLabel +%106 = OpPhi %v4float %66 %62 %50 %63 +OpStore %_entryPointOutput_vColor %106 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%67 = OpFunction %void None %68 +%69 = OpFunctionParameter %uint +%70 = OpFunctionParameter %uint +%71 = OpFunctionParameter %uint +%72 = OpFunctionParameter %uint +%73 = OpLabel +%75 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_0 +%76 = OpAtomicIAdd %uint %75 %uint_4 %uint_0 %uint_9 +%77 = OpIAdd %uint %76 %uint_9 +%78 = OpArrayLength %uint %74 1 +%79 = OpULessThanEqual %bool %77 %78 +OpSelectionMerge %80 None +OpBranchConditional %79 %81 %80 +%81 = OpLabel +%82 = OpIAdd %uint %76 %uint_0 +%83 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %82 +OpStore %83 %uint_9 +%84 = OpIAdd %uint %76 %uint_1 +%85 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %84 +OpStore %85 %uint_23 +%86 = OpIAdd %uint %76 %uint_2 +%87 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %86 +OpStore %87 %69 +%88 = OpIAdd %uint %76 %uint_3 +%89 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %88 +OpStore %89 %uint_4 +%90 = OpLoad %v4float %gl_FragCoord +%91 = OpBitcast %v4uint %90 +%92 = OpCompositeExtract %uint %91 0 +%93 = OpIAdd %uint %76 %uint_4 +%94 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %93 +OpStore %94 %92 +%95 = OpCompositeExtract %uint %91 1 +%96 = OpIAdd %uint %76 %uint_5 +%97 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %96 +OpStore %97 %95 +%98 = OpIAdd %uint %76 %uint_6 +%99 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %98 +OpStore %99 %70 +%100 = OpIAdd %uint %76 %uint_7 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %100 +OpStore %101 %71 +%102 = OpIAdd %uint %76 %uint_8 +%103 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %102 +OpStore %103 %72 +OpBranch %80 +%80 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck<InstBindlessCheckPass>( + defs_before + func_before, defs_after + func_after + output_func, true, + true); +} + +TEST_F(InstBindlessTest, InstrumentOpImage) { + // This test verifies that the pass will correctly instrument shader + // using OpImage. This test was created by editing the SPIR-V + // from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +OpCapability StorageImageReadWithoutFormat +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%v2int = OpTypeVector %int 2 +%int_0 = OpConstant %int 0 +%20 = OpTypeImage %float 2D 0 0 0 0 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%39 = OpTypeSampledImage %20 +%_arr_39_uint_128 = OpTypeArray %39 %uint_128 +%_ptr_UniformConstant__arr_39_uint_128 = OpTypePointer UniformConstant %_arr_39_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_39_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_39 = OpTypePointer UniformConstant %39 +%_ptr_Input_v2int = OpTypePointer Input %v2int +%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability StorageImageReadWithoutFormat +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_struct_51 Block +OpMemberDecorate %_struct_51 0 Offset 0 +OpMemberDecorate %_struct_51 1 Offset 4 +OpDecorate %53 DescriptorSet 7 +OpDecorate %53 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%v2int = OpTypeVector %int 2 +%int_0 = OpConstant %int 0 +%15 = OpTypeImage %float 2D 0 0 0 0 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%18 = OpTypeSampledImage %15 +%_arr_18_uint_128 = OpTypeArray %18 %uint_128 +%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 +%_ptr_Input_v2int = OpTypePointer Input %v2int +%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%44 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_51 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_51 = OpTypePointer StorageBuffer %_struct_51 +%53 = OpVariable %_ptr_StorageBuffer__struct_51 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_9 = OpConstant %uint 9 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_51 = OpConstant %uint 51 +%99 = OpConstantNull %v4float +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %v2int %i_vTextureCoords +%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpAccessChain %_ptr_UniformConstant_39 %g_tColor %64 +%66 = OpLoad %39 %65 +%75 = OpImage %20 %66 +%71 = OpImageRead %v4float %75 %53 +OpStore %_entryPointOutput_vColor %71 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %9 +%26 = OpLabel +%27 = OpLoad %v2int %i_vTextureCoords +%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%29 = OpLoad %uint %28 +%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29 +%31 = OpLoad %18 %30 +%32 = OpImage %15 %31 +%36 = OpULessThan %bool %29 %uint_128 +OpSelectionMerge %37 None +OpBranchConditional %36 %38 %39 +%38 = OpLabel +%40 = OpLoad %18 %30 +%41 = OpImage %15 %40 +%42 = OpImageRead %v4float %41 %27 +OpBranch %37 +%39 = OpLabel +%98 = OpFunctionCall %void %43 %uint_51 %uint_0 %29 %uint_128 +OpBranch %37 +%37 = OpLabel +%100 = OpPhi %v4float %42 %38 %99 %39 +OpStore %_entryPointOutput_vColor %100 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%43 = OpFunction %void None %44 +%45 = OpFunctionParameter %uint +%46 = OpFunctionParameter %uint +%47 = OpFunctionParameter %uint +%48 = OpFunctionParameter %uint +%49 = OpLabel +%55 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_0 +%58 = OpAtomicIAdd %uint %55 %uint_4 %uint_0 %uint_9 +%59 = OpIAdd %uint %58 %uint_9 +%60 = OpArrayLength %uint %53 1 +%61 = OpULessThanEqual %bool %59 %60 +OpSelectionMerge %62 None +OpBranchConditional %61 %63 %62 +%63 = OpLabel +%64 = OpIAdd %uint %58 %uint_0 +%66 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %64 +OpStore %66 %uint_9 +%68 = OpIAdd %uint %58 %uint_1 +%69 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %68 +OpStore %69 %uint_23 +%71 = OpIAdd %uint %58 %uint_2 +%72 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %71 +OpStore %72 %45 +%74 = OpIAdd %uint %58 %uint_3 +%75 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %74 +OpStore %75 %uint_4 +%78 = OpLoad %v4float %gl_FragCoord +%80 = OpBitcast %v4uint %78 +%81 = OpCompositeExtract %uint %80 0 +%82 = OpIAdd %uint %58 %uint_4 +%83 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %82 +OpStore %83 %81 +%84 = OpCompositeExtract %uint %80 1 +%86 = OpIAdd %uint %58 %uint_5 +%87 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %86 +OpStore %87 %84 +%89 = OpIAdd %uint %58 %uint_6 +%90 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %89 +OpStore %90 %46 +%92 = OpIAdd %uint %58 %uint_7 +%93 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %92 +OpStore %93 %47 +%95 = OpIAdd %uint %58 %uint_8 +%96 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %95 +OpStore %96 %48 +OpBranch %62 +%62 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck<InstBindlessCheckPass>( + defs_before + func_before, defs_after + func_after + output_func, true, + true); +} + +TEST_F(InstBindlessTest, InstrumentSampledImage) { + // This test verifies that the pass will correctly instrument shader + // using sampled image. This test was created by editing the SPIR-V + // from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%20 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%39 = OpTypeSampledImage %20 +%_arr_39_uint_128 = OpTypeArray %39 %uint_128 +%_ptr_UniformConstant__arr_39_uint_128 = OpTypePointer UniformConstant %_arr_39_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_39_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_39 = OpTypePointer UniformConstant %39 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_struct_49 Block +OpMemberDecorate %_struct_49 0 Offset 0 +OpMemberDecorate %_struct_49 1 Offset 4 +OpDecorate %51 DescriptorSet 7 +OpDecorate %51 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%15 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%18 = OpTypeSampledImage %15 +%_arr_18_uint_128 = OpTypeArray %18 %uint_128 +%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%42 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_49 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_49 = OpTypePointer StorageBuffer %_struct_49 +%51 = OpVariable %_ptr_StorageBuffer__struct_49 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_9 = OpConstant %uint 9 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_49 = OpConstant %uint 49 +%97 = OpConstantNull %v4float +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %v2float %i_vTextureCoords +%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpAccessChain %_ptr_UniformConstant_39 %g_tColor %64 +%66 = OpLoad %39 %65 +%71 = OpImageSampleImplicitLod %v4float %66 %53 +OpStore %_entryPointOutput_vColor %71 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %9 +%26 = OpLabel +%27 = OpLoad %v2float %i_vTextureCoords +%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%29 = OpLoad %uint %28 +%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29 +%31 = OpLoad %18 %30 +%35 = OpULessThan %bool %29 %uint_128 +OpSelectionMerge %36 None +OpBranchConditional %35 %37 %38 +%37 = OpLabel +%39 = OpLoad %18 %30 +%40 = OpImageSampleImplicitLod %v4float %39 %27 +OpBranch %36 +%38 = OpLabel +%96 = OpFunctionCall %void %41 %uint_49 %uint_0 %29 %uint_128 +OpBranch %36 +%36 = OpLabel +%98 = OpPhi %v4float %40 %37 %97 %38 +OpStore %_entryPointOutput_vColor %98 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%41 = OpFunction %void None %42 +%43 = OpFunctionParameter %uint +%44 = OpFunctionParameter %uint +%45 = OpFunctionParameter %uint +%46 = OpFunctionParameter %uint +%47 = OpLabel +%53 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_0 +%56 = OpAtomicIAdd %uint %53 %uint_4 %uint_0 %uint_9 +%57 = OpIAdd %uint %56 %uint_9 +%58 = OpArrayLength %uint %51 1 +%59 = OpULessThanEqual %bool %57 %58 +OpSelectionMerge %60 None +OpBranchConditional %59 %61 %60 +%61 = OpLabel +%62 = OpIAdd %uint %56 %uint_0 +%64 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %62 +OpStore %64 %uint_9 +%66 = OpIAdd %uint %56 %uint_1 +%67 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %66 +OpStore %67 %uint_23 +%69 = OpIAdd %uint %56 %uint_2 +%70 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %69 +OpStore %70 %43 +%72 = OpIAdd %uint %56 %uint_3 +%73 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %72 +OpStore %73 %uint_4 +%76 = OpLoad %v4float %gl_FragCoord +%78 = OpBitcast %v4uint %76 +%79 = OpCompositeExtract %uint %78 0 +%80 = OpIAdd %uint %56 %uint_4 +%81 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %80 +OpStore %81 %79 +%82 = OpCompositeExtract %uint %78 1 +%84 = OpIAdd %uint %56 %uint_5 +%85 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %84 +OpStore %85 %82 +%87 = OpIAdd %uint %56 %uint_6 +%88 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %87 +OpStore %88 %44 +%90 = OpIAdd %uint %56 %uint_7 +%91 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %90 +OpStore %91 %45 +%93 = OpIAdd %uint %56 %uint_8 +%94 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %93 +OpStore %94 %46 +OpBranch %60 +%60 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck<InstBindlessCheckPass>( + defs_before + func_before, defs_after + func_after + output_func, true, + true); +} + +TEST_F(InstBindlessTest, InstrumentImageWrite) { + // This test verifies that the pass will correctly instrument shader + // doing bindless image write. This test was created by editing the SPIR-V + // from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +OpCapability StorageImageWriteWithoutFormat +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%v2int = OpTypeVector %int 2 +%int_0 = OpConstant %int 0 +%20 = OpTypeImage %float 2D 0 0 0 0 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%80 = OpConstantNull %v4float +%_arr_20_uint_128 = OpTypeArray %20 %uint_128 +%_ptr_UniformConstant__arr_20_uint_128 = OpTypePointer UniformConstant %_arr_20_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_20_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 +%_ptr_Input_v2int = OpTypePointer Input %v2int +%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability StorageImageWriteWithoutFormat +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_struct_48 Block +OpMemberDecorate %_struct_48 0 Offset 0 +OpMemberDecorate %_struct_48 1 Offset 4 +OpDecorate %50 DescriptorSet 7 +OpDecorate %50 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%v2int = OpTypeVector %int 2 +%int_0 = OpConstant %int 0 +%16 = OpTypeImage %float 2D 0 0 0 0 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%19 = OpConstantNull %v4float +%_arr_16_uint_128 = OpTypeArray %16 %uint_128 +%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 +%_ptr_Input_v2int = OpTypePointer Input %v2int +%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%41 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_48 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_48 = OpTypePointer StorageBuffer %_struct_48 +%50 = OpVariable %_ptr_StorageBuffer__struct_48 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_9 = OpConstant %uint 9 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_51 = OpConstant %uint 51 +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %v2int %i_vTextureCoords +%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64 +%66 = OpLoad %20 %65 +OpImageWrite %66 %53 %80 +OpStore %_entryPointOutput_vColor %80 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %9 +%27 = OpLabel +%28 = OpLoad %v2int %i_vTextureCoords +%29 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%30 = OpLoad %uint %29 +%31 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %30 +%32 = OpLoad %16 %31 +%35 = OpULessThan %bool %30 %uint_128 +OpSelectionMerge %36 None +OpBranchConditional %35 %37 %38 +%37 = OpLabel +%39 = OpLoad %16 %31 +OpImageWrite %39 %28 %19 +OpBranch %36 +%38 = OpLabel +%95 = OpFunctionCall %void %40 %uint_51 %uint_0 %30 %uint_128 +OpBranch %36 +%36 = OpLabel +OpStore %_entryPointOutput_vColor %19 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%40 = OpFunction %void None %41 +%42 = OpFunctionParameter %uint +%43 = OpFunctionParameter %uint +%44 = OpFunctionParameter %uint +%45 = OpFunctionParameter %uint +%46 = OpLabel +%52 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_0 +%55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_9 +%56 = OpIAdd %uint %55 %uint_9 +%57 = OpArrayLength %uint %50 1 +%58 = OpULessThanEqual %bool %56 %57 +OpSelectionMerge %59 None +OpBranchConditional %58 %60 %59 +%60 = OpLabel +%61 = OpIAdd %uint %55 %uint_0 +%63 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %61 +OpStore %63 %uint_9 +%65 = OpIAdd %uint %55 %uint_1 +%66 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %65 +OpStore %66 %uint_23 +%68 = OpIAdd %uint %55 %uint_2 +%69 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %68 +OpStore %69 %42 +%71 = OpIAdd %uint %55 %uint_3 +%72 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %71 +OpStore %72 %uint_4 +%75 = OpLoad %v4float %gl_FragCoord +%77 = OpBitcast %v4uint %75 +%78 = OpCompositeExtract %uint %77 0 +%79 = OpIAdd %uint %55 %uint_4 +%80 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %79 +OpStore %80 %78 +%81 = OpCompositeExtract %uint %77 1 +%83 = OpIAdd %uint %55 %uint_5 +%84 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %83 +OpStore %84 %81 +%86 = OpIAdd %uint %55 %uint_6 +%87 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %86 +OpStore %87 %43 +%89 = OpIAdd %uint %55 %uint_7 +%90 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %89 +OpStore %90 %44 +%92 = OpIAdd %uint %55 %uint_8 +%93 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %92 +OpStore %93 %45 +OpBranch %59 +%59 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck<InstBindlessCheckPass>( + defs_before + func_before, defs_after + func_after + output_func, true, + true); +} + +TEST_F(InstBindlessTest, InstrumentVertexSimple) { + // This test verifies that the pass will correctly instrument shader + // doing bindless image write. This test was created by editing the SPIR-V + // from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %_ %coords2D +OpSource GLSL 450 +OpName %main "main" +OpName %lod "lod" +OpName %coords1D "coords1D" +OpName %gl_PerVertex "gl_PerVertex" +OpMemberName %gl_PerVertex 0 "gl_Position" +OpMemberName %gl_PerVertex 1 "gl_PointSize" +OpMemberName %gl_PerVertex 2 "gl_ClipDistance" +OpMemberName %gl_PerVertex 3 "gl_CullDistance" +OpName %_ "" +OpName %texSampler1D "texSampler1D" +OpName %foo "foo" +OpMemberName %foo 0 "g_idx" +OpName %__0 "" +OpName %coords2D "coords2D" +OpMemberDecorate %gl_PerVertex 0 BuiltIn Position +OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize +OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance +OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance +OpDecorate %gl_PerVertex Block +OpDecorate %texSampler1D DescriptorSet 0 +OpDecorate %texSampler1D Binding 3 +OpMemberDecorate %foo 0 Offset 0 +OpDecorate %foo Block +OpDecorate %__0 DescriptorSet 0 +OpDecorate %__0 Binding 5 +OpDecorate %coords2D Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_3 = OpConstant %float 3 +%float_1_78900003 = OpConstant %float 1.78900003 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex +%_ = OpVariable %_ptr_Output_gl_PerVertex Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%21 = OpTypeImage %float 1D 0 0 0 1 Unknown +%22 = OpTypeSampledImage %21 +%uint_128 = OpConstant %uint 128 +%_arr_22_uint_128 = OpTypeArray %22 %uint_128 +%_ptr_UniformConstant__arr_22_uint_128 = OpTypePointer UniformConstant %_arr_22_uint_128 +%texSampler1D = OpVariable %_ptr_UniformConstant__arr_22_uint_128 UniformConstant +%foo = OpTypeStruct %int +%_ptr_Uniform_foo = OpTypePointer Uniform %foo +%__0 = OpVariable %_ptr_Uniform_foo Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%coords2D = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability Sampled1D +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %_ %coords2D %gl_VertexID %gl_InstanceID +OpSource GLSL 450 +OpName %main "main" +OpName %lod "lod" +OpName %coords1D "coords1D" +OpName %gl_PerVertex "gl_PerVertex" +OpMemberName %gl_PerVertex 0 "gl_Position" +OpMemberName %gl_PerVertex 1 "gl_PointSize" +OpMemberName %gl_PerVertex 2 "gl_ClipDistance" +OpMemberName %gl_PerVertex 3 "gl_CullDistance" +OpName %_ "" +OpName %texSampler1D "texSampler1D" +OpName %foo "foo" +OpMemberName %foo 0 "g_idx" +OpName %__0 "" +OpName %coords2D "coords2D" +OpMemberDecorate %gl_PerVertex 0 BuiltIn Position +OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize +OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance +OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance +OpDecorate %gl_PerVertex Block +OpDecorate %texSampler1D DescriptorSet 0 +OpDecorate %texSampler1D Binding 3 +OpMemberDecorate %foo 0 Offset 0 +OpDecorate %foo Block +OpDecorate %__0 DescriptorSet 0 +OpDecorate %__0 Binding 5 +OpDecorate %coords2D Location 0 +OpDecorate %_struct_61 Block +OpMemberDecorate %_struct_61 0 Offset 0 +OpMemberDecorate %_struct_61 1 Offset 4 +OpDecorate %63 DescriptorSet 7 +OpDecorate %63 Binding 0 +OpDecorate %gl_VertexID BuiltIn VertexId +OpDecorate %gl_InstanceID BuiltIn InstanceId +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_3 = OpConstant %float 3 +%float_1_78900003 = OpConstant %float 1.78900003 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex +%_ = OpVariable %_ptr_Output_gl_PerVertex Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%24 = OpTypeImage %float 1D 0 0 0 1 Unknown +%25 = OpTypeSampledImage %24 +%uint_128 = OpConstant %uint 128 +%_arr_25_uint_128 = OpTypeArray %25 %uint_128 +%_ptr_UniformConstant__arr_25_uint_128 = OpTypePointer UniformConstant %_arr_25_uint_128 +%texSampler1D = OpVariable %_ptr_UniformConstant__arr_25_uint_128 UniformConstant +%foo = OpTypeStruct %int +%_ptr_Uniform_foo = OpTypePointer Uniform %foo +%__0 = OpVariable %_ptr_Uniform_foo Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%coords2D = OpVariable %_ptr_Input_v2float Input +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%54 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_61 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_61 = OpTypePointer StorageBuffer %_struct_61 +%63 = OpVariable %_ptr_StorageBuffer__struct_61 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_9 = OpConstant %uint 9 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_uint = OpTypePointer Input %uint +%gl_VertexID = OpVariable %_ptr_Input_uint Input +%gl_InstanceID = OpVariable %_ptr_Input_uint Input +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_74 = OpConstant %uint 74 +%106 = OpConstantNull %v4float +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%lod = OpVariable %_ptr_Function_float Function +%coords1D = OpVariable %_ptr_Function_float Function +OpStore %lod %float_3 +OpStore %coords1D %float_1_78900003 +%31 = OpAccessChain %_ptr_Uniform_int %__0 %int_0 +%32 = OpLoad %int %31 +%34 = OpAccessChain %_ptr_UniformConstant_22 %texSampler1D %32 +%35 = OpLoad %22 %34 +%36 = OpLoad %float %coords1D +%37 = OpLoad %float %lod +%38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37 +%40 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +OpStore %40 %38 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %12 +%35 = OpLabel +%lod = OpVariable %_ptr_Function_float Function +%coords1D = OpVariable %_ptr_Function_float Function +OpStore %lod %float_3 +OpStore %coords1D %float_1_78900003 +%36 = OpAccessChain %_ptr_Uniform_int %__0 %int_0 +%37 = OpLoad %int %36 +%38 = OpAccessChain %_ptr_UniformConstant_25 %texSampler1D %37 +%39 = OpLoad %25 %38 +%40 = OpLoad %float %coords1D +%41 = OpLoad %float %lod +%46 = OpULessThan %bool %37 %uint_128 +OpSelectionMerge %47 None +OpBranchConditional %46 %48 %49 +%48 = OpLabel +%50 = OpLoad %25 %38 +%51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41 +OpBranch %47 +%49 = OpLabel +%52 = OpBitcast %uint %37 +%105 = OpFunctionCall %void %53 %uint_74 %uint_0 %52 %uint_128 +OpBranch %47 +%47 = OpLabel +%107 = OpPhi %v4float %51 %48 %106 %49 +%43 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +OpStore %43 %107 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%53 = OpFunction %void None %54 +%55 = OpFunctionParameter %uint +%56 = OpFunctionParameter %uint +%57 = OpFunctionParameter %uint +%58 = OpFunctionParameter %uint +%59 = OpLabel +%65 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_0 +%68 = OpAtomicIAdd %uint %65 %uint_4 %uint_0 %uint_9 +%69 = OpIAdd %uint %68 %uint_9 +%70 = OpArrayLength %uint %63 1 +%71 = OpULessThanEqual %bool %69 %70 +OpSelectionMerge %72 None +OpBranchConditional %71 %73 %72 +%73 = OpLabel +%74 = OpIAdd %uint %68 %uint_0 +%75 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %74 +OpStore %75 %uint_9 +%77 = OpIAdd %uint %68 %uint_1 +%78 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %77 +OpStore %78 %uint_23 +%80 = OpIAdd %uint %68 %uint_2 +%81 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %80 +OpStore %81 %55 +%83 = OpIAdd %uint %68 %uint_3 +%84 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %83 +OpStore %84 %uint_0 +%87 = OpLoad %uint %gl_VertexID +%88 = OpIAdd %uint %68 %uint_4 +%89 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %88 +OpStore %89 %87 +%91 = OpLoad %uint %gl_InstanceID +%93 = OpIAdd %uint %68 %uint_5 +%94 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %93 +OpStore %94 %91 +%96 = OpIAdd %uint %68 %uint_6 +%97 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %96 +OpStore %97 %56 +%99 = OpIAdd %uint %68 %uint_7 +%100 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %99 +OpStore %100 %57 +%102 = OpIAdd %uint %68 %uint_8 +%103 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %102 +OpStore %103 %58 +OpBranch %72 +%72 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck<InstBindlessCheckPass>( + defs_before + func_before, defs_after + func_after + output_func, true, + true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// TODO(greg-lunarg): Come up with cases to put here :) + +} // namespace +} // namespace opt +} // namespace spvtools
diff --git a/test/opt/ir_builder.cpp b/test/opt/ir_builder.cpp index 41d7f57..8231bdc 100644 --- a/test/opt/ir_builder.cpp +++ b/test/opt/ir_builder.cpp
@@ -317,20 +317,20 @@ InstructionBuilder builder(context.get(), &*context->module()->begin()->begin()->begin()); - EXPECT_NE(nullptr, builder.Add32BitUnsignedIntegerConstant(13)); - EXPECT_NE(nullptr, builder.Add32BitSignedIntegerConstant(-1)); + EXPECT_NE(nullptr, builder.GetUintConstant(13)); + EXPECT_NE(nullptr, builder.GetSintConstant(-1)); // Try adding the same constants again to make sure they aren't added. - EXPECT_NE(nullptr, builder.Add32BitUnsignedIntegerConstant(13)); - EXPECT_NE(nullptr, builder.Add32BitSignedIntegerConstant(-1)); + EXPECT_NE(nullptr, builder.GetUintConstant(13)); + EXPECT_NE(nullptr, builder.GetSintConstant(-1)); // Try adding different constants to make sure the type is reused. - EXPECT_NE(nullptr, builder.Add32BitUnsignedIntegerConstant(1)); - EXPECT_NE(nullptr, builder.Add32BitSignedIntegerConstant(34)); + EXPECT_NE(nullptr, builder.GetUintConstant(1)); + EXPECT_NE(nullptr, builder.GetSintConstant(34)); // Try adding 0 as both signed and unsigned. - EXPECT_NE(nullptr, builder.Add32BitUnsignedIntegerConstant(0)); - EXPECT_NE(nullptr, builder.Add32BitSignedIntegerConstant(0)); + EXPECT_NE(nullptr, builder.GetUintConstant(0)); + EXPECT_NE(nullptr, builder.GetSintConstant(0)); Match(text, context.get()); } @@ -362,25 +362,25 @@ InstructionBuilder builder(context.get(), &*context->module()->begin()->begin()->begin()); - Instruction* const_1 = builder.Add32BitUnsignedIntegerConstant(13); - Instruction* const_2 = builder.Add32BitSignedIntegerConstant(-1); + Instruction* const_1 = builder.GetUintConstant(13); + Instruction* const_2 = builder.GetSintConstant(-1); EXPECT_NE(nullptr, const_1); EXPECT_NE(nullptr, const_2); // Try adding the same constants again to make sure they aren't added. - EXPECT_EQ(const_1, builder.Add32BitUnsignedIntegerConstant(13)); - EXPECT_EQ(const_2, builder.Add32BitSignedIntegerConstant(-1)); + EXPECT_EQ(const_1, builder.GetUintConstant(13)); + EXPECT_EQ(const_2, builder.GetSintConstant(-1)); - Instruction* const_3 = builder.Add32BitUnsignedIntegerConstant(1); - Instruction* const_4 = builder.Add32BitSignedIntegerConstant(34); + Instruction* const_3 = builder.GetUintConstant(1); + Instruction* const_4 = builder.GetSintConstant(34); // Try adding different constants to make sure the type is reused. EXPECT_NE(nullptr, const_3); EXPECT_NE(nullptr, const_4); - Instruction* const_5 = builder.Add32BitUnsignedIntegerConstant(0); - Instruction* const_6 = builder.Add32BitSignedIntegerConstant(0); + Instruction* const_5 = builder.GetUintConstant(0); + Instruction* const_6 = builder.GetSintConstant(0); // Try adding 0 as both signed and unsigned. EXPECT_NE(nullptr, const_5);
diff --git a/test/opt/loop_optimizations/peeling.cpp b/test/opt/loop_optimizations/peeling.cpp index 7c6a84e..10d8add 100644 --- a/test/opt/loop_optimizations/peeling.cpp +++ b/test/opt/loop_optimizations/peeling.cpp
@@ -132,7 +132,7 @@ } else { InstructionBuilder builder(context.get(), &*f.begin()); // Exit condition. - loop_count = builder.Add32BitSignedIntegerConstant(10); + loop_count = builder.GetSintConstant(10); } LoopPeeling peel(&*ld.begin(), loop_count); @@ -494,7 +494,7 @@ InstructionBuilder builder(context.get(), &*f.begin()); // Exit condition. - Instruction* ten_cst = builder.Add32BitSignedIntegerConstant(10); + Instruction* ten_cst = builder.GetSintConstant(10); LoopPeeling peel(&*ld.begin(), ten_cst); EXPECT_TRUE(peel.CanPeelLoop()); @@ -548,7 +548,7 @@ InstructionBuilder builder(context.get(), &*f.begin()); // Exit condition. - Instruction* ten_cst = builder.Add32BitSignedIntegerConstant(10); + Instruction* ten_cst = builder.GetSintConstant(10); LoopPeeling peel(&*ld.begin(), ten_cst); EXPECT_TRUE(peel.CanPeelLoop()); @@ -604,7 +604,7 @@ InstructionBuilder builder(context.get(), &*f.begin()); // Exit condition. - Instruction* ten_cst = builder.Add32BitSignedIntegerConstant(10); + Instruction* ten_cst = builder.GetSintConstant(10); LoopPeeling peel(&*ld.begin(), ten_cst, context->get_def_use_mgr()->GetDef(22)); @@ -657,7 +657,7 @@ InstructionBuilder builder(context.get(), &*f.begin()); // Exit condition. - Instruction* ten_cst = builder.Add32BitSignedIntegerConstant(10); + Instruction* ten_cst = builder.GetSintConstant(10); LoopPeeling peel(&*ld.begin(), ten_cst, context->get_def_use_mgr()->GetDef(22)); @@ -918,7 +918,7 @@ EXPECT_EQ(ld.NumLoops(), 1u); InstructionBuilder builder(context.get(), &*f.begin()); // Exit condition. - Instruction* ten_cst = builder.Add32BitUnsignedIntegerConstant(10); + Instruction* ten_cst = builder.GetUintConstant(10); LoopPeeling peel(&*ld.begin(), ten_cst); EXPECT_TRUE(peel.CanPeelLoop()); @@ -968,7 +968,7 @@ InstructionBuilder builder(context.get(), &*f.begin()); // Exit condition. - Instruction* ten_cst = builder.Add32BitUnsignedIntegerConstant(10); + Instruction* ten_cst = builder.GetUintConstant(10); LoopPeeling peel(&*ld.begin(), ten_cst); EXPECT_TRUE(peel.CanPeelLoop());