Updated spirv-tools.

This commit is contained in:
Бранимир Караџић 2019-08-22 20:01:58 -07:00
parent cf5bf3c0e5
commit 5907520a19
50 changed files with 2811 additions and 298 deletions

View File

@ -117,6 +117,7 @@ SPVTOOLS_OPT_SRC_FILES := \
source/opt/inline_exhaustive_pass.cpp \
source/opt/inline_opaque_pass.cpp \
source/opt/inst_bindless_check_pass.cpp \
source/opt/inst_buff_addr_check_pass.cpp \
source/opt/instruction.cpp \
source/opt/instruction_list.cpp \
source/opt/instrument_pass.cpp \
@ -171,7 +172,8 @@ SPVTOOLS_OPT_SRC_FILES := \
source/opt/upgrade_memory_model.cpp \
source/opt/value_number_table.cpp \
source/opt/vector_dce.cpp \
source/opt/workaround1209.cpp
source/opt/workaround1209.cpp \
source/opt/wrap_opkill.cpp
# Locations of grammar files.
#

View File

@ -535,6 +535,8 @@ static_library("spvtools_opt") {
"source/opt/inline_pass.h",
"source/opt/inst_bindless_check_pass.cpp",
"source/opt/inst_bindless_check_pass.h",
"source/opt/inst_buff_addr_check_pass.cpp",
"source/opt/inst_buff_addr_check_pass.h",
"source/opt/instruction.cpp",
"source/opt/instruction.h",
"source/opt/instruction_list.cpp",
@ -650,6 +652,8 @@ static_library("spvtools_opt") {
"source/opt/vector_dce.h",
"source/opt/workaround1209.cpp",
"source/opt/workaround1209.h",
"source/opt/wrap_opkill.cpp",
"source/opt/wrap_opkill.h",
]
deps = [

View File

@ -18,7 +18,7 @@ v2019.4 2019-08-08
- Handle nested breaks from switches. (#2624)
- Optimizer: Handle array type with OpSpecConstantOp length (#2652)
- Perform merge return with single return in loop. (#2714)
- Add —preserve-bindings and —preserve-spec-constants (#2693)
- Add --preserve-bindings and --preserve-spec-constants (#2693)
- Remove Common Uniform Elimination Pass (#2731)
- Allow ray tracing shaders in inst bindle check pass. (#2733)
- Add pass to inject code for robust-buffer-access semantics (#2771)

View File

@ -1 +1 @@
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-2-g95386f9e"
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-16-g8336d192"

View File

@ -23,8 +23,9 @@
// communicate with shaders instrumented by passes created by:
//
// CreateInstBindlessCheckPass
// CreateInstBuffAddrCheckPass
//
// More detailed documentation of this routine can be found in optimizer.hpp
// More detailed documentation of these routines can be found in optimizer.hpp
namespace spvtools {
@ -157,6 +158,12 @@ static const int kInst2BindlessUninitOutDescIndex = kInst2StageOutCnt + 1;
static const int kInst2BindlessUninitOutUnused = kInst2StageOutCnt + 2;
static const int kInst2BindlessUninitOutCnt = kInst2StageOutCnt + 3;
// A buffer address unalloc error will output the 64-bit pointer in
// two 32-bit pieces, lower bits first.
static const int kInst2BuffAddrUnallocOutDescPtrLo = kInst2StageOutCnt + 1;
static const int kInst2BuffAddrUnallocOutDescPtrHi = kInst2StageOutCnt + 2;
static const int kInst2BuffAddrUnallocOutCnt = kInst2StageOutCnt + 3;
// DEPRECATED
static const int kInstBindlessOutDescIndex = kInstStageOutCnt + 1;
static const int kInstBindlessOutDescBound = kInstStageOutCnt + 2;
@ -171,6 +178,7 @@ static const int kInst2MaxOutCnt = kInst2StageOutCnt + 3;
// These are the possible validation error codes.
static const int kInstErrorBindlessBounds = 0;
static const int kInstErrorBindlessUninit = 1;
static const int kInstErrorBuffAddrUnallocRef = 2;
// Direct Input Buffer Offsets
//
@ -187,14 +195,16 @@ static const int kDebugInputDataOffset = 0;
// These are the bindings for the different buffers which are
// read or written by the instrumentation passes.
//
// This is the output buffer written by InstBindlessCheckPass
// and possibly other future validations.
// This is the output buffer written by InstBindlessCheckPass,
// InstBuffAddrCheckPass, and possibly other future validations.
static const int kDebugOutputBindingStream = 0;
// The binding for the input buffer read by InstBindlessCheckPass and
// possibly other future validations.
// The binding for the input buffer read by InstBindlessCheckPass.
static const int kDebugInputBindingBindless = 1;
// The binding for the input buffer read by InstBuffAddrCheckPass.
static const int kDebugInputBindingBuffAddr = 2;
// Bindless Validation Input Buffer Format
//
// An input buffer for bindless validation consists of a single array of
@ -216,6 +226,31 @@ static const int kDebugInputBindlessOffsetReserved = 0;
// Data[ Data[ s + kDebugInputBindlessOffsetLengths ] + b ]
static const int kDebugInputBindlessOffsetLengths = 1;
// Buffer Device Address Input Buffer Format
//
// An input buffer for buffer device address validation consists of a single
// array of unsigned 64-bit integers we will call Data[]. This array is
// formatted as follows:
//
// At offset kDebugInputBuffAddrPtrOffset is a list of sorted valid buffer
// addresses. The list is terminated with the address 0xffffffffffffffff.
// If 0x0 is not a valid buffer address, this address is inserted at the
// start of the list.
//
static const int kDebugInputBuffAddrPtrOffset = 1;
//
// At offset kDebugInputBuffAddrLengthOffset in Data[] is a single uint64 which
// gives an offset to the start of the buffer length data. More
// specifically, for a buffer whose pointer is located at input buffer offset
// i, the length is located at:
//
// Data[ i - kDebugInputBuffAddrPtrOffset
// + Data[ kDebugInputBuffAddrLengthOffset ] ]
//
// The length associated with the 0xffffffffffffffff address is zero. If
// not a valid buffer, the length associated with the 0x0 address is zero.
static const int kDebugInputBuffAddrLengthOffset = 0;
} // namespace spvtools
#endif // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_

View File

@ -729,6 +729,30 @@ Optimizer::PassToken CreateInstBindlessCheckPass(
uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false,
bool input_init_enable = false, uint32_t version = 1);
// Create a pass to instrument physical buffer address checking
// This pass instruments all physical buffer address references to check that
// all referenced bytes fall in a valid buffer. If the reference is
// invalid, a record is written to the debug output buffer (if space allows)
// and a null value is returned. This pass is designed to support buffer
// address validation in the Vulkan validation layers.
//
// Dead code elimination should be run after this pass as the original,
// potentially invalid code is not removed and could cause undefined behavior,
// including crashes. Instruction simplification would likely also be
// beneficial. It is also generally recommended that this pass (and all
// instrumentation passes) be run after any legalization and optimization
// passes. This will give better analysis for the instrumentation and avoid
// potentially de-optimizing the instrument code, for example, inlining
// the debug record output function throughout the module.
//
// The instrumentation will read and write buffers in debug
// descriptor set |desc_set|. It will write |shader_id| in each output record
// to identify the shader module which generated the record.
// |version| specifies the output buffer record format.
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
uint32_t shader_id,
uint32_t version = 2);
// Create a pass to upgrade to the VulkanKHR memory model.
// This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR.
// Additionally, it modifies memory, image, atomic and barrier operations to
@ -795,6 +819,10 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass();
// for the first index.
Optimizer::PassToken CreateDescriptorScalarReplacementPass();
// Create a pass to replace all OpKill instruction with a function call to a
// function that has a single OpKill. This allows more code to be inlined.
Optimizer::PassToken CreateWrapOpKillPass();
} // namespace spvtools
#endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_

View File

@ -55,6 +55,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
inline_opaque_pass.h
inline_pass.h
inst_bindless_check_pass.h
inst_buff_addr_check_pass.h
instruction.h
instruction_list.h
instrument_pass.h
@ -113,6 +114,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
value_number_table.h
vector_dce.h
workaround1209.h
wrap_opkill.h
aggressive_dead_code_elim_pass.cpp
basic_block.cpp
@ -157,6 +159,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
inline_opaque_pass.cpp
inline_pass.cpp
inst_bindless_check_pass.cpp
inst_buff_addr_check_pass.cpp
instruction.cpp
instruction_list.cpp
instrument_pass.cpp
@ -212,6 +215,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
value_number_table.cpp
vector_dce.cpp
workaround1209.cpp
wrap_opkill.cpp
)
if(MSVC)

View File

@ -527,6 +527,9 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
pointer_type->storage_class());
uint32_t new_pointer_type_id =
context()->get_type_mgr()->GetTypeInstruction(&pointerTy);
if (new_pointer_type_id == 0) {
return false;
}
if (new_pointer_type_id != use->type_id()) {
return CanUpdateUses(use, new_pointer_type_id);
@ -542,6 +545,9 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
const analysis::Type* new_type =
type_mgr->GetMemberType(type, access_chain);
uint32_t new_type_id = type_mgr->GetTypeInstruction(new_type);
if (new_type_id == 0) {
return false;
}
if (new_type_id != use->type_id()) {
return CanUpdateUses(use, new_type_id);

View File

@ -14,7 +14,7 @@
#include "source/opt/desc_sroa.h"
#include <source/util/string_utils.h>
#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
@ -248,6 +248,24 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
context()->AddAnnotationInst(std::move(new_decoration));
}
// Create a new OpName for the replacement variable.
for (auto p : context()->GetNames(var->result_id())) {
Instruction* name_inst = p.second;
std::string name_str = utils::MakeString(name_inst->GetOperand(1).words);
name_str += "[";
name_str += utils::ToString(idx);
name_str += "]";
std::unique_ptr<Instruction> new_name(new Instruction(
context(), SpvOpName, 0, 0,
std::initializer_list<Operand>{
{SPV_OPERAND_TYPE_ID, {id}},
{SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
Instruction* new_name_inst = new_name.get();
context()->AddDebug2Inst(std::move(new_name));
get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst);
}
return id;
}

View File

@ -77,10 +77,6 @@ class GraphicsRobustAccessPass : public Pass {
// type. Ignores any value bits beyond the width of the type.
Instruction* GetValueForType(uint64_t value, const analysis::Integer* type);
// Returns the unsigned value for the given constant. Assumes it's at most
// 64 bits wide.
uint64_t GetUnsignedValueForConstant(const analysis::Constant* c);
// Converts an integer value to an unsigned wider integer type, using either
// sign extension or zero extension. The new instruction is inserted
// immediately before |before_inst|, and is analyzed for definitions and uses.

View File

@ -0,0 +1,427 @@
// Copyright (c) 2019 The Khronos Group Inc.
// Copyright (c) 2019 Valve Corporation
// Copyright (c) 2019 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "inst_buff_addr_check_pass.h"
namespace spvtools {
namespace opt {
uint32_t InstBuffAddrCheckPass::CloneOriginalReference(
Instruction* ref_inst, InstructionBuilder* builder) {
// Clone original ref with new result id (if load)
assert(
(ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) &&
"unexpected ref");
std::unique_ptr<Instruction> new_ref_inst(ref_inst->Clone(context()));
uint32_t ref_result_id = ref_inst->result_id();
uint32_t new_ref_id = 0;
if (ref_result_id != 0) {
new_ref_id = TakeNextId();
new_ref_inst->SetResultId(new_ref_id);
}
// Register new reference and add to new block
Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst));
uid2offset_[added_inst->unique_id()] = uid2offset_[ref_inst->unique_id()];
if (new_ref_id != 0)
get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id);
return new_ref_id;
}
bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) {
if (ref_inst->opcode() != SpvOpLoad && ref_inst->opcode() != SpvOpStore)
return false;
uint32_t ptr_id = ref_inst->GetSingleWordInOperand(0);
analysis::DefUseManager* du_mgr = get_def_use_mgr();
Instruction* ptr_inst = du_mgr->GetDef(ptr_id);
if (ptr_inst->opcode() != SpvOpAccessChain) return false;
uint32_t ptr_ty_id = ptr_inst->type_id();
Instruction* ptr_ty_inst = du_mgr->GetDef(ptr_ty_id);
if (ptr_ty_inst->GetSingleWordInOperand(0) !=
SpvStorageClassPhysicalStorageBufferEXT)
return false;
return true;
}
// TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ??
void InstBuffAddrCheckPass::GenCheckCode(
uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id,
uint32_t stage_idx, Instruction* ref_inst,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
BasicBlock* back_blk_ptr = &*new_blocks->back();
InstructionBuilder builder(
context(), back_blk_ptr,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
// Gen conditional branch on check_id. Valid branch generates original
// reference. Invalid generates debug output and zero result (if needed).
uint32_t merge_blk_id = TakeNextId();
uint32_t valid_blk_id = TakeNextId();
uint32_t invalid_blk_id = TakeNextId();
std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
std::unique_ptr<Instruction> valid_label(NewLabel(valid_blk_id));
std::unique_ptr<Instruction> invalid_label(NewLabel(invalid_blk_id));
(void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id,
merge_blk_id, SpvSelectionControlMaskNone);
// Gen valid branch
std::unique_ptr<BasicBlock> new_blk_ptr(
new BasicBlock(std::move(valid_label)));
builder.SetInsertPoint(&*new_blk_ptr);
uint32_t new_ref_id = CloneOriginalReference(ref_inst, &builder);
(void)builder.AddBranch(merge_blk_id);
new_blocks->push_back(std::move(new_blk_ptr));
// Gen invalid block
new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
builder.SetInsertPoint(&*new_blk_ptr);
// Convert uptr from uint64 to 2 uint32
Instruction* lo_uptr_inst =
builder.AddUnaryOp(GetUintId(), SpvOpUConvert, ref_uptr_id);
Instruction* rshift_uptr_inst =
builder.AddBinaryOp(GetUint64Id(), SpvOpShiftRightLogical, ref_uptr_id,
builder.GetUintConstantId(32));
Instruction* hi_uptr_inst = builder.AddUnaryOp(GetUintId(), SpvOpUConvert,
rshift_uptr_inst->result_id());
GenDebugStreamWrite(
uid2offset_[ref_inst->unique_id()], stage_idx,
{error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()},
&builder);
// Gen zero for invalid reference
uint32_t ref_type_id = ref_inst->type_id();
(void)builder.AddBranch(merge_blk_id);
new_blocks->push_back(std::move(new_blk_ptr));
// Gen merge block
new_blk_ptr.reset(new BasicBlock(std::move(merge_label)));
builder.SetInsertPoint(&*new_blk_ptr);
// Gen phi of new reference and zero, if necessary, and replace the
// result id of the original reference with that of the Phi. Kill original
// reference.
if (new_ref_id != 0) {
Instruction* phi_inst = builder.AddPhi(
ref_type_id, {new_ref_id, valid_blk_id, builder.GetNullId(ref_type_id),
invalid_blk_id});
context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id());
}
new_blocks->push_back(std::move(new_blk_ptr));
context()->KillInst(ref_inst);
}
uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) {
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
switch (type_inst->opcode()) {
case SpvOpTypeFloat:
case SpvOpTypeInt:
return type_inst->GetSingleWordInOperand(0) / 8u;
case SpvOpTypeVector:
case SpvOpTypeMatrix:
return type_inst->GetSingleWordInOperand(1) *
GetTypeLength(type_inst->GetSingleWordInOperand(0));
case SpvOpTypePointer:
assert(type_inst->GetSingleWordInOperand(0) ==
SpvStorageClassPhysicalStorageBufferEXT &&
"unexpected pointer type");
return 8u;
default:
assert(false && "unexpected buffer reference type");
return 0;
}
}
void InstBuffAddrCheckPass::AddParam(uint32_t type_id,
std::vector<uint32_t>* param_vec,
std::unique_ptr<Function>* input_func) {
uint32_t pid = TakeNextId();
param_vec->push_back(pid);
std::unique_ptr<Instruction> param_inst(new Instruction(
get_module()->context(), SpvOpFunctionParameter, type_id, pid, {}));
get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst);
(*input_func)->AddParameter(std::move(param_inst));
}
uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() {
if (search_test_func_id_ == 0) {
// Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)"
// which searches input buffer for buffer which most likely contains the
// pointer value |ref_ptr| and verifies that the entire reference of
// length |len| bytes is contained in the buffer.
search_test_func_id_ = TakeNextId();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
std::vector<const analysis::Type*> param_types = {
type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())};
analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types);
analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
std::unique_ptr<Instruction> func_inst(
new Instruction(get_module()->context(), SpvOpFunction, GetBoolId(),
search_test_func_id_,
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
{SpvFunctionControlMaskNone}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{type_mgr->GetTypeInstruction(reg_func_ty)}}}));
get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
std::unique_ptr<Function> input_func =
MakeUnique<Function>(std::move(func_inst));
std::vector<uint32_t> param_vec;
// Add ref_ptr and length parameters
AddParam(GetUint64Id(), &param_vec, &input_func);
AddParam(GetUintId(), &param_vec, &input_func);
// Empty first block.
uint32_t first_blk_id = TakeNextId();
std::unique_ptr<Instruction> first_blk_label(NewLabel(first_blk_id));
std::unique_ptr<BasicBlock> first_blk_ptr =
MakeUnique<BasicBlock>(std::move(first_blk_label));
InstructionBuilder builder(
context(), &*first_blk_ptr,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
uint32_t hdr_blk_id = TakeNextId();
// Branch to search loop header
std::unique_ptr<Instruction> hdr_blk_label(NewLabel(hdr_blk_id));
(void)builder.AddInstruction(MakeUnique<Instruction>(
context(), SpvOpBranch, 0, 0,
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {hdr_blk_id}}}));
first_blk_ptr->SetParent(&*input_func);
input_func->AddBasicBlock(std::move(first_blk_ptr));
// Linear search loop header block
// TODO(greg-lunarg): Implement binary search
std::unique_ptr<BasicBlock> hdr_blk_ptr =
MakeUnique<BasicBlock>(std::move(hdr_blk_label));
builder.SetInsertPoint(&*hdr_blk_ptr);
// Phi for search index. Starts with 1.
uint32_t cont_blk_id = TakeNextId();
std::unique_ptr<Instruction> cont_blk_label(NewLabel(cont_blk_id));
// Deal with def-use cycle caused by search loop index computation.
// Create Add and Phi instructions first, then do Def analysis on Add.
// Add Phi and Add instructions and do Use analysis later.
uint32_t idx_phi_id = TakeNextId();
uint32_t idx_inc_id = TakeNextId();
std::unique_ptr<Instruction> idx_inc_inst(new Instruction(
context(), SpvOpIAdd, GetUintId(), idx_inc_id,
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{builder.GetUintConstantId(1u)}}}));
std::unique_ptr<Instruction> idx_phi_inst(new Instruction(
context(), SpvOpPhi, GetUintId(), idx_phi_id,
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{builder.GetUintConstantId(1u)}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst);
// Add (previously created) search index phi
(void)builder.AddInstruction(std::move(idx_phi_inst));
// LoopMerge
uint32_t bound_test_blk_id = TakeNextId();
std::unique_ptr<Instruction> bound_test_blk_label(
NewLabel(bound_test_blk_id));
(void)builder.AddInstruction(MakeUnique<Instruction>(
context(), SpvOpLoopMerge, 0, 0,
std::initializer_list<Operand>{
{SPV_OPERAND_TYPE_ID, {bound_test_blk_id}},
{SPV_OPERAND_TYPE_ID, {cont_blk_id}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvLoopControlMaskNone}}}));
// Branch to continue/work block
(void)builder.AddInstruction(MakeUnique<Instruction>(
context(), SpvOpBranch, 0, 0,
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
hdr_blk_ptr->SetParent(&*input_func);
input_func->AddBasicBlock(std::move(hdr_blk_ptr));
// Continue/Work Block. Read next buffer pointer and break if greater
// than ref_ptr arg.
std::unique_ptr<BasicBlock> cont_blk_ptr =
MakeUnique<BasicBlock>(std::move(cont_blk_label));
builder.SetInsertPoint(&*cont_blk_ptr);
// Add (previously created) search index increment now.
(void)builder.AddInstruction(std::move(idx_inc_inst));
// Load next buffer address from debug input buffer
uint32_t ibuf_id = GetInputBufferId();
uint32_t ibuf_ptr_id = GetInputBufferPtrId();
Instruction* uptr_ac_inst = builder.AddTernaryOp(
ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id);
uint32_t ibuf_type_id = GetInputBufferTypeId();
Instruction* uptr_load_inst =
builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, uptr_ac_inst->result_id());
// If loaded address greater than ref_ptr arg, break, else branch back to
// loop header
Instruction* uptr_test_inst =
builder.AddBinaryOp(GetBoolId(), SpvOpUGreaterThan,
uptr_load_inst->result_id(), param_vec[0]);
(void)builder.AddConditionalBranch(uptr_test_inst->result_id(),
bound_test_blk_id, hdr_blk_id,
kInvalidId, SpvSelectionControlMaskNone);
cont_blk_ptr->SetParent(&*input_func);
input_func->AddBasicBlock(std::move(cont_blk_ptr));
// Bounds test block. Read length of selected buffer and test that
// all len arg bytes are in buffer.
std::unique_ptr<BasicBlock> bound_test_blk_ptr =
MakeUnique<BasicBlock>(std::move(bound_test_blk_label));
builder.SetInsertPoint(&*bound_test_blk_ptr);
// Decrement index to point to previous/candidate buffer address
Instruction* cand_idx_inst = builder.AddBinaryOp(
GetUintId(), SpvOpISub, idx_inc_id, builder.GetUintConstantId(1u));
// Load candidate buffer address
Instruction* cand_ac_inst =
builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
builder.GetUintConstantId(kDebugInputDataOffset),
cand_idx_inst->result_id());
Instruction* cand_load_inst =
builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, cand_ac_inst->result_id());
// Compute offset of ref_ptr from candidate buffer address
Instruction* offset_inst = builder.AddBinaryOp(
ibuf_type_id, SpvOpISub, param_vec[0], cand_load_inst->result_id());
// Convert ref length to uint64
Instruction* ref_len_64_inst =
builder.AddUnaryOp(ibuf_type_id, SpvOpUConvert, param_vec[1]);
// Add ref length to ref offset to compute end of reference
Instruction* ref_end_inst =
builder.AddBinaryOp(ibuf_type_id, SpvOpIAdd, offset_inst->result_id(),
ref_len_64_inst->result_id());
// Load starting index of lengths in input buffer and convert to uint32
Instruction* len_start_ac_inst =
builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
builder.GetUintConstantId(kDebugInputDataOffset),
builder.GetUintConstantId(0u));
Instruction* len_start_load_inst = builder.AddUnaryOp(
ibuf_type_id, SpvOpLoad, len_start_ac_inst->result_id());
Instruction* len_start_32_inst = builder.AddUnaryOp(
GetUintId(), SpvOpUConvert, len_start_load_inst->result_id());
// Decrement search index to get candidate buffer length index
Instruction* cand_len_idx_inst =
builder.AddBinaryOp(GetUintId(), SpvOpISub, cand_idx_inst->result_id(),
builder.GetUintConstantId(1u));
// Add candidate length index to start index
Instruction* len_idx_inst = builder.AddBinaryOp(
GetUintId(), SpvOpIAdd, cand_len_idx_inst->result_id(),
len_start_32_inst->result_id());
// Load candidate buffer length
Instruction* len_ac_inst =
builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
builder.GetUintConstantId(kDebugInputDataOffset),
len_idx_inst->result_id());
Instruction* len_load_inst =
builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, len_ac_inst->result_id());
// Test if reference end within candidate buffer length
Instruction* len_test_inst = builder.AddBinaryOp(
GetBoolId(), SpvOpULessThanEqual, ref_end_inst->result_id(),
len_load_inst->result_id());
// Return test result
(void)builder.AddInstruction(MakeUnique<Instruction>(
context(), SpvOpReturnValue, 0, 0,
std::initializer_list<Operand>{
{SPV_OPERAND_TYPE_ID, {len_test_inst->result_id()}}}));
// Close block
bound_test_blk_ptr->SetParent(&*input_func);
input_func->AddBasicBlock(std::move(bound_test_blk_ptr));
// Close function and add function to module
std::unique_ptr<Instruction> func_end_inst(
new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {}));
get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
input_func->SetFunctionEnd(std::move(func_end_inst));
context()->AddFunction(std::move(input_func));
}
return search_test_func_id_;
}
uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst,
InstructionBuilder* builder,
uint32_t* ref_uptr_id) {
// Enable Int64 if necessary
if (!get_feature_mgr()->HasCapability(SpvCapabilityInt64)) {
std::unique_ptr<Instruction> cap_int64_inst(new Instruction(
context(), SpvOpCapability, 0, 0,
std::initializer_list<Operand>{
{SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityInt64}}}));
get_def_use_mgr()->AnalyzeInstDefUse(&*cap_int64_inst);
get_module()->AddCapability(std::move(cap_int64_inst));
}
// Convert reference pointer to uint64
uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0);
Instruction* ref_uptr_inst =
builder->AddUnaryOp(GetUint64Id(), SpvOpConvertPtrToU, ref_ptr_id);
*ref_uptr_id = ref_uptr_inst->result_id();
// Compute reference length in bytes
analysis::DefUseManager* du_mgr = get_def_use_mgr();
Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id);
uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id();
Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id);
uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1));
uint32_t ref_len_id = builder->GetUintConstantId(ref_len);
// Gen call to search and test function
const std::vector<uint32_t> args = {GetSearchAndTestFuncId(), *ref_uptr_id,
ref_len_id};
Instruction* call_inst =
builder->AddNaryOp(GetBoolId(), SpvOpFunctionCall, args);
uint32_t retval = call_inst->result_id();
return retval;
}
void InstBuffAddrCheckPass::GenBuffAddrCheckCode(
BasicBlock::iterator ref_inst_itr,
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
// Look for reference through indexed descriptor. If found, analyze and
// save components. If not, return.
Instruction* ref_inst = &*ref_inst_itr;
if (!IsPhysicalBuffAddrReference(ref_inst)) return;
// Move original block's preceding instructions into first new block
std::unique_ptr<BasicBlock> new_blk_ptr;
MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
InstructionBuilder builder(
context(), &*new_blk_ptr,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
new_blocks->push_back(std::move(new_blk_ptr));
uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef);
// Generate code to do search and test if all bytes of reference
// are within a listed buffer. Return reference pointer converted to uint64.
uint32_t ref_uptr_id;
uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id);
// Generate test of search results with true branch
// being full reference and false branch being debug output and zero
// for the referenced value.
GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst,
new_blocks);
// Move original block's remaining code into remainder/merge block and add
// to new blocks
BasicBlock* back_blk_ptr = &*new_blocks->back();
MovePostludeCode(ref_block_itr, back_blk_ptr);
}
void InstBuffAddrCheckPass::InitInstBuffAddrCheck() {
// Initialize base class
InitializeInstrument();
// Initialize class
search_test_func_id_ = 0;
}
Pass::Status InstBuffAddrCheckPass::ProcessImpl() {
// Perform bindless bounds check on each entry point function in module
InstProcessFunction pfn =
[this](BasicBlock::iterator ref_inst_itr,
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
new_blocks);
};
bool modified = InstProcessEntryPointCallTree(pfn);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
Pass::Status InstBuffAddrCheckPass::Process() {
if (!get_feature_mgr()->HasCapability(
SpvCapabilityPhysicalStorageBufferAddressesEXT))
return Status::SuccessWithoutChange;
InitInstBuffAddrCheck();
return ProcessImpl();
}
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,133 @@
// Copyright (c) 2019 The Khronos Group Inc.
// Copyright (c) 2019 Valve Corporation
// Copyright (c) 2019 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_
#define LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_
#include "instrument_pass.h"
namespace spvtools {
namespace opt {
// This class/pass is designed to support the GPU-assisted validation layer of
// the Buffer Device Address (BDA) extension in
// https://github.com/KhronosGroup/Vulkan-ValidationLayers. The internal and
// external design of this class may change as the layer evolves.
class InstBuffAddrCheckPass : public InstrumentPass {
public:
// For test harness only
InstBuffAddrCheckPass()
: InstrumentPass(7, 23, kInstValidationIdBuffAddr, 1) {}
// For all other interfaces
InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version)
: InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr,
version) {}
~InstBuffAddrCheckPass() override = default;
// See optimizer.hpp for pass user documentation.
Status Process() override;
const char* name() const override { return "inst-bindless-check-pass"; }
private:
// Return byte length of type |type_id|. Must be int, float, vector, matrix
// or physical pointer.
uint32_t GetTypeLength(uint32_t type_id);
// Add |type_id| param to |input_func| and add id to |param_vec|.
void AddParam(uint32_t type_id, std::vector<uint32_t>* param_vec,
std::unique_ptr<Function>* input_func);
// Return id for search and test function. Generate it if not already gen'd.
uint32_t GetSearchAndTestFuncId();
// Generate code into |builder| to do search of the BDA debug input buffer
// for the buffer used by |ref_inst| and test that all bytes of reference
// are within the buffer. Returns id of boolean value which is true if
// search and test is successful, false otherwise.
uint32_t GenSearchAndTest(Instruction* ref_inst, InstructionBuilder* builder,
uint32_t* ref_uptr_id);
// This function does checking instrumentation on a single
// instruction which references through a physical storage buffer address.
// GenBuffAddrCheckCode generates code that checks that all bytes that
// are referenced fall within a buffer that was queried via
// the Vulkan API call vkGetBufferDeviceAddressEXT().
//
// The function is designed to be passed to
// InstrumentPass::InstProcessEntryPointCallTree(), which applies the
// function to each instruction in a module and replaces the instruction
// with instrumented code if warranted.
//
// If |ref_inst_itr| is a physical storage buffer reference, return in
// |new_blocks| the result of instrumenting it with validation code within
// its block at |ref_block_itr|. The validation code first executes a check
// for the specific condition called for. If the check passes, it executes
// the remainder of the reference, otherwise writes a record to the debug
// output buffer stream including |function_idx, instruction_idx, stage_idx|
// and replaces the reference with the null value of the original type. The
// block at |ref_block_itr| can just be replaced with the blocks in
// |new_blocks|, which will contain at least two blocks. The last block will
// comprise all instructions following |ref_inst_itr|,
// preceded by a phi instruction if needed.
//
// This instrumentation function utilizes GenDebugStreamWrite() to write its
// error records. The validation-specific part of the error record will
// have the format:
//
// Validation Error Code (=kInstErrorBuffAddr)
// Buffer Address (lowest 32 bits)
// Buffer Address (highest 32 bits)
//
void GenBuffAddrCheckCode(
BasicBlock::iterator ref_inst_itr,
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
// Return true if |ref_inst| is a physical buffer address reference, false
// otherwise.
bool IsPhysicalBuffAddrReference(Instruction* ref_inst);
// Clone original reference |ref_inst| into |builder| and return id of result
uint32_t CloneOriginalReference(Instruction* ref_inst,
InstructionBuilder* builder);
// Generate instrumentation code for boolean test result |check_id|,
// adding new blocks to |new_blocks|. Generate conditional branch to valid
// or invalid reference blocks. Generate valid reference block which does
// original reference |ref_inst|. Then generate invalid reference block which
// writes debug error output utilizing |ref_inst|, |error_id| and
// |stage_idx|. Generate merge block for valid and invalid reference blocks.
// Kill original reference.
void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t length_id,
uint32_t stage_idx, Instruction* ref_inst,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
// Initialize state for instrumenting physical buffer address checking
void InitInstBuffAddrCheck();
// Apply GenBuffAddrCheckCode to every instruction in module.
Pass::Status ProcessImpl();
// Id of search and test function, if already gen'd, else zero.
uint32_t search_test_func_id_;
};
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_

View File

@ -107,7 +107,7 @@ void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id,
builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id,
builder->GetUintConstantId(field_offset));
uint32_t buf_id = GetOutputBufferId();
uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
Instruction* achain_inst =
builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
builder->GetUintConstantId(kDebugOutputDataOffset),
@ -373,19 +373,33 @@ void InstrumentPass::UpdateSucceedingPhis(
});
}
// Return id for output buffer uint ptr type
uint32_t InstrumentPass::GetBufferUintPtrId() {
if (buffer_uint_ptr_id_ == 0) {
buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
uint32_t InstrumentPass::GetOutputBufferPtrId() {
if (output_buffer_ptr_id_ == 0) {
output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
GetUintId(), SpvStorageClassStorageBuffer);
}
return buffer_uint_ptr_id_;
return output_buffer_ptr_id_;
}
uint32_t InstrumentPass::GetInputBufferTypeId() {
return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id()
: GetUintId();
}
uint32_t InstrumentPass::GetInputBufferPtrId() {
if (input_buffer_ptr_id_ == 0) {
input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
GetInputBufferTypeId(), SpvStorageClassStorageBuffer);
}
return input_buffer_ptr_id_;
}
uint32_t InstrumentPass::GetOutputBufferBinding() {
switch (validation_id_) {
case kInstValidationIdBindless:
return kDebugOutputBindingStream;
case kInstValidationIdBuffAddr:
return kDebugOutputBindingStream;
default:
assert(false && "unexpected validation id");
}
@ -396,20 +410,24 @@ uint32_t InstrumentPass::GetInputBufferBinding() {
switch (validation_id_) {
case kInstValidationIdBindless:
return kDebugInputBindingBindless;
case kInstValidationIdBuffAddr:
return kDebugInputBindingBuffAddr;
default:
assert(false && "unexpected validation id");
}
return 0;
}
analysis::Type* InstrumentPass::GetUintRuntimeArrayType(
analysis::DecorationManager* deco_mgr, analysis::TypeManager* type_mgr) {
if (uint_rarr_ty_ == nullptr) {
analysis::Integer uint_ty(32, false);
analysis::Type* InstrumentPass::GetUintXRuntimeArrayType(
uint32_t width, analysis::Type** rarr_ty) {
if (*rarr_ty == nullptr) {
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Integer uint_ty(width, false);
analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty);
uint_rarr_ty_ = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp);
uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(uint_rarr_ty_);
*rarr_ty = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp);
uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(*rarr_ty);
// By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of
// a block, and will therefore be decorated with an ArrayStride. Therefore
// the undecorated type returned here will not be pre-existing and can
@ -418,9 +436,16 @@ analysis::Type* InstrumentPass::GetUintRuntimeArrayType(
// invalidated after this pass.
assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 &&
"used RuntimeArray type returned");
deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u);
deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride,
width / 8u);
}
return uint_rarr_ty_;
return *rarr_ty;
}
analysis::Type* InstrumentPass::GetUintRuntimeArrayType(uint32_t width) {
analysis::Type** rarr_ty =
(width == 64) ? &uint64_rarr_ty_ : &uint32_rarr_ty_;
return GetUintXRuntimeArrayType(width, rarr_ty);
}
void InstrumentPass::AddStorageBufferExt() {
@ -445,8 +470,7 @@ uint32_t InstrumentPass::GetOutputBufferId() {
// If not created yet, create one
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Type* reg_uint_rarr_ty =
GetUintRuntimeArrayType(deco_mgr, type_mgr);
analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(32);
analysis::Integer uint_ty(32, false);
analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty});
@ -494,8 +518,8 @@ uint32_t InstrumentPass::GetInputBufferId() {
// If not created yet, create one
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Type* reg_uint_rarr_ty =
GetUintRuntimeArrayType(deco_mgr, type_mgr);
uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u;
analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width);
analysis::Struct buf_ty({reg_uint_rarr_ty});
analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty);
uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
@ -555,6 +579,16 @@ uint32_t InstrumentPass::GetUintId() {
return uint_id_;
}
uint32_t InstrumentPass::GetUint64Id() {
if (uint64_id_ == 0) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Integer uint64_ty(64, false);
analysis::Type* reg_uint64_ty = type_mgr->GetRegisteredType(&uint64_ty);
uint64_id_ = type_mgr->GetTypeInstruction(reg_uint64_ty);
}
return uint64_id_;
}
uint32_t InstrumentPass::GetVecUintId(uint32_t len) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Integer uint_ty(32, false);
@ -642,7 +676,7 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
(version_ == 1) ? kInstStageOutCnt : kInst2StageOutCnt;
uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt;
uint32_t buf_id = GetOutputBufferId();
uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
Instruction* obuf_curr_sz_ac_inst =
builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
builder.GetUintConstantId(kDebugOutputSizeOffset));
@ -713,16 +747,17 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
uint32_t func_id = param2input_func_id_[param_cnt];
if (func_id != 0) return func_id;
// Create input function for param_cnt
// Create input function for param_cnt.
func_id = TakeNextId();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
std::vector<const analysis::Type*> param_types;
for (uint32_t c = 0; c < param_cnt; ++c)
param_types.push_back(type_mgr->GetType(GetUintId()));
analysis::Function func_ty(type_mgr->GetType(GetUintId()), param_types);
uint32_t ibuf_type_id = GetInputBufferTypeId();
analysis::Function func_ty(type_mgr->GetType(ibuf_type_id), param_types);
analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
std::unique_ptr<Instruction> func_inst(new Instruction(
get_module()->context(), SpvOpFunction, GetUintId(), func_id,
get_module()->context(), SpvOpFunction, ibuf_type_id, func_id,
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
{SpvFunctionControlMaskNone}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
@ -752,22 +787,27 @@ uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
// loaded value if it exists, and load value from input buffer at new offset.
// Return last loaded value.
uint32_t buf_id = GetInputBufferId();
uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
uint32_t buf_ptr_id = GetInputBufferPtrId();
uint32_t last_value_id = 0;
for (uint32_t p = 0; p < param_cnt; ++p) {
uint32_t offset_id;
if (p == 0) {
offset_id = param_vec[0];
} else {
if (ibuf_type_id != GetUintId()) {
Instruction* ucvt_inst =
builder.AddUnaryOp(GetUintId(), SpvOpUConvert, last_value_id);
last_value_id = ucvt_inst->result_id();
}
Instruction* offset_inst = builder.AddBinaryOp(
GetUintId(), SpvOpIAdd, last_value_id, param_vec[p]);
offset_id = offset_inst->result_id();
}
Instruction* ac_inst = builder.AddTernaryOp(
buf_uint_ptr_id, SpvOpAccessChain, buf_id,
buf_ptr_id, SpvOpAccessChain, buf_id,
builder.GetUintConstantId(kDebugInputDataOffset), offset_id);
Instruction* load_inst =
builder.AddUnaryOp(GetUintId(), SpvOpLoad, ac_inst->result_id());
builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, ac_inst->result_id());
last_value_id = load_inst->result_id();
}
(void)builder.AddInstruction(MakeUnique<Instruction>(
@ -894,18 +934,21 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
void InstrumentPass::InitializeInstrument() {
output_buffer_id_ = 0;
buffer_uint_ptr_id_ = 0;
output_buffer_ptr_id_ = 0;
input_buffer_ptr_id_ = 0;
output_func_id_ = 0;
output_func_param_cnt_ = 0;
input_buffer_id_ = 0;
v4float_id_ = 0;
uint_id_ = 0;
uint64_id_ = 0;
v4uint_id_ = 0;
v3uint_id_ = 0;
bool_id_ = 0;
void_id_ = 0;
storage_buffer_ext_defined_ = false;
uint_rarr_ty_ = nullptr;
uint32_rarr_ty_ = nullptr;
uint64_rarr_ty_ = nullptr;
// clear collections
id2function_.clear();

View File

@ -60,6 +60,7 @@ namespace opt {
// These are used to identify the general validation being done and map to
// its output buffers.
static const uint32_t kInstValidationIdBindless = 0;
static const uint32_t kInstValidationIdBuffAddr = 1;
class InstrumentPass : public Pass {
using cbb_ptr = const BasicBlock*;
@ -217,6 +218,9 @@ class InstrumentPass : public Pass {
// Return id for 32-bit unsigned type
uint32_t GetUintId();
// Return id for 32-bit unsigned type
uint32_t GetUint64Id();
// Return id for 32-bit unsigned type
uint32_t GetBoolId();
@ -224,11 +228,20 @@ class InstrumentPass : public Pass {
uint32_t GetVoidId();
// Return pointer to type for runtime array of uint
analysis::Type* GetUintRuntimeArrayType(analysis::DecorationManager* deco_mgr,
analysis::TypeManager* type_mgr);
analysis::Type* GetUintXRuntimeArrayType(uint32_t width,
analysis::Type** rarr_ty);
// Return pointer to type for runtime array of uint
analysis::Type* GetUintRuntimeArrayType(uint32_t width);
// Return id for buffer uint type
uint32_t GetBufferUintPtrId();
uint32_t GetOutputBufferPtrId();
// Return id for buffer uint type
uint32_t GetInputBufferTypeId();
// Return id for buffer uint type
uint32_t GetInputBufferPtrId();
// Return binding for output buffer for current validation.
uint32_t GetOutputBufferBinding();
@ -354,8 +367,11 @@ class InstrumentPass : public Pass {
// id for output buffer variable
uint32_t output_buffer_id_;
// type id for output buffer element
uint32_t buffer_uint_ptr_id_;
// ptr type id for output buffer element
uint32_t output_buffer_ptr_id_;
// ptr type id for input buffer element
uint32_t input_buffer_ptr_id_;
// id for debug output function
uint32_t output_func_id_;
@ -381,6 +397,9 @@ class InstrumentPass : public Pass {
// id for 32-bit unsigned type
uint32_t uint_id_;
// id for 32-bit unsigned type
uint32_t uint64_id_;
// id for bool type
uint32_t bool_id_;
@ -394,7 +413,10 @@ class InstrumentPass : public Pass {
bool storage_buffer_ext_defined_;
// runtime array of uint type
analysis::Type* uint_rarr_ty_;
analysis::Type* uint64_rarr_ty_;
// runtime array of uint type
analysis::Type* uint32_rarr_ty_;
// Pre-instrumentation same-block insts
std::unordered_map<uint32_t, Instruction*> same_block_pre_;

View File

@ -465,6 +465,23 @@ class InstructionBuilder {
return AddInstruction(std::move(new_inst));
}
Instruction* AddFunctionCall(uint32_t result_type, uint32_t function,
const std::vector<uint32_t>& parameters) {
std::vector<Operand> operands;
operands.push_back({SPV_OPERAND_TYPE_ID, {function}});
for (uint32_t id : parameters) {
operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
}
uint32_t result_id = GetContext()->TakeNextId();
if (result_id == 0) {
return nullptr;
}
std::unique_ptr<Instruction> new_inst(new Instruction(
GetContext(), SpvOpFunctionCall, result_type, result_id, operands));
return AddInstruction(std::move(new_inst));
}
// Inserts the new instruction before the insertion point.
Instruction* AddInstruction(std::unique_ptr<Instruction>&& insn) {
Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));
@ -512,6 +529,10 @@ class InstructionBuilder {
// Returns true if the users requested to update |analysis|.
inline bool IsAnalysisUpdateRequested(IRContext::Analysis analysis) const {
if (!GetContext()->AreAnalysesValid(analysis)) {
// Do not try to update something that is not built.
return false;
}
return preserved_analyses_ & analysis;
}

View File

@ -107,8 +107,10 @@ Optimizer& Optimizer::RegisterPass(PassToken&& p) {
// or enable more copy propagation.
Optimizer& Optimizer::RegisterLegalizationPasses() {
return
// Remove unreachable block so that merge return works.
RegisterPass(CreateDeadBranchElimPass())
// Wrap OpKill instructions so all other code can be inlined.
RegisterPass(CreateWrapOpKillPass())
// Remove unreachable block so that merge return works.
.RegisterPass(CreateDeadBranchElimPass())
// Merge the returns so we can inline.
.RegisterPass(CreateMergeReturnPass())
// Make sure uses and definitions are in the same function.
@ -154,7 +156,8 @@ Optimizer& Optimizer::RegisterLegalizationPasses() {
}
Optimizer& Optimizer::RegisterPerformancePasses() {
return RegisterPass(CreateDeadBranchElimPass())
return RegisterPass(CreateWrapOpKillPass())
.RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(CreateMergeReturnPass())
.RegisterPass(CreateInlineExhaustivePass())
.RegisterPass(CreateAggressiveDCEPass())
@ -190,7 +193,8 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
}
Optimizer& Optimizer::RegisterSizePasses() {
return RegisterPass(CreateDeadBranchElimPass())
return RegisterPass(CreateWrapOpKillPass())
.RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(CreateMergeReturnPass())
.RegisterPass(CreateInlineExhaustivePass())
.RegisterPass(CreateAggressiveDCEPass())
@ -396,11 +400,20 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
} else if (pass_name == "replace-invalid-opcode") {
RegisterPass(CreateReplaceInvalidOpcodePass());
} else if (pass_name == "inst-bindless-check") {
RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2));
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
RegisterPass(CreateAggressiveDCEPass());
} else if (pass_name == "inst-desc-idx-check") {
RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true, 2));
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
RegisterPass(CreateAggressiveDCEPass());
} else if (pass_name == "inst-buff-addr-check") {
RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2));
RegisterPass(CreateAggressiveDCEPass());
} else if (pass_name == "simplify-instructions") {
RegisterPass(CreateSimplificationPass());
} else if (pass_name == "ssa-rewrite") {
@ -477,6 +490,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterPass(CreateDecomposeInitializedVariablesPass());
} else if (pass_name == "graphics-robust-access") {
RegisterPass(CreateGraphicsRobustAccessPass());
} else if (pass_name == "wrap-opkill") {
RegisterPass(CreateWrapOpKillPass());
} else {
Errorf(consumer(), nullptr, {},
"Unknown flag '--%s'. Use --help for a list of valid flags",
@ -853,6 +868,13 @@ Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
input_init_enable, version));
}
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
uint32_t shader_id,
uint32_t version) {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
}
Optimizer::PassToken CreateCodeSinkingPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::CodeSinkingPass>());
@ -893,4 +915,8 @@ Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
MakeUnique<opt::DescriptorScalarReplacement>());
}
Optimizer::PassToken CreateWrapOpKillPass() {
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
}
} // namespace spvtools

View File

@ -43,7 +43,8 @@ Pass::Status Pass::Run(IRContext* ctx) {
if (status == Status::SuccessWithChange) {
ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses());
}
assert(ctx->IsConsistent());
assert((status == Status::Failure || ctx->IsConsistent()) &&
"An analysis in the context is out of date.");
return status;
}

View File

@ -43,6 +43,7 @@
#include "source/opt/inline_exhaustive_pass.h"
#include "source/opt/inline_opaque_pass.h"
#include "source/opt/inst_bindless_check_pass.h"
#include "source/opt/inst_buff_addr_check_pass.h"
#include "source/opt/legalize_vector_shuffle_pass.h"
#include "source/opt/licm_pass.h"
#include "source/opt/local_access_chain_convert_pass.h"
@ -76,5 +77,6 @@
#include "source/opt/upgrade_memory_model.h"
#include "source/opt/vector_dce.h"
#include "source/opt/workaround1209.h"
#include "source/opt/wrap_opkill.h"
#endif // SOURCE_OPT_PASSES_H_

View File

@ -58,7 +58,9 @@ Pass::Status PrivateToLocalPass::Process() {
modified = !variables_to_move.empty();
for (auto p : variables_to_move) {
MoveVariable(p.first, p.second);
if (!MoveVariable(p.first, p.second)) {
return Status::Failure;
}
localized_variables.insert(p.first->result_id());
}
@ -112,7 +114,7 @@ Function* PrivateToLocalPass::FindLocalFunction(const Instruction& inst) const {
return target_function;
} // namespace opt
void PrivateToLocalPass::MoveVariable(Instruction* variable,
bool PrivateToLocalPass::MoveVariable(Instruction* variable,
Function* function) {
// The variable needs to be removed from the global section, and placed in the
// header of the function. First step remove from the global list.
@ -125,6 +127,9 @@ void PrivateToLocalPass::MoveVariable(Instruction* variable,
// Update the type as well.
uint32_t new_type_id = GetNewType(variable->type_id());
if (new_type_id == 0) {
return false;
}
variable->SetResultType(new_type_id);
// Place the variable at the start of the first basic block.
@ -133,7 +138,7 @@ void PrivateToLocalPass::MoveVariable(Instruction* variable,
function->begin()->begin()->InsertBefore(move(var));
// Update uses where the type may have changed.
UpdateUses(variable->result_id());
return UpdateUses(variable->result_id());
}
uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
@ -143,7 +148,9 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
old_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx);
uint32_t new_type_id =
type_mgr->FindPointerToType(pointee_type_id, SpvStorageClassFunction);
context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id));
if (new_type_id != 0) {
context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id));
}
return new_type_id;
}
@ -168,7 +175,7 @@ bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
}
}
void PrivateToLocalPass::UpdateUse(Instruction* inst) {
bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
// The cases in this switch have to match the cases in |IsValidUse|. If we
// don't think it is valid, the optimization will not view the variable as a
// candidate, and therefore the use will not be updated.
@ -179,14 +186,20 @@ void PrivateToLocalPass::UpdateUse(Instruction* inst) {
// The type is fine because it is the type pointed to, and that does not
// change.
break;
case SpvOpAccessChain:
case SpvOpAccessChain: {
context()->ForgetUses(inst);
inst->SetResultType(GetNewType(inst->type_id()));
uint32_t new_type_id = GetNewType(inst->type_id());
if (new_type_id == 0) {
return false;
}
inst->SetResultType(new_type_id);
context()->AnalyzeUses(inst);
// Update uses where the type may have changed.
UpdateUses(inst->result_id());
break;
if (!UpdateUses(inst->result_id())) {
return false;
}
} break;
case SpvOpName:
case SpvOpEntryPoint: // entry points will be updated separately.
break;
@ -195,15 +208,20 @@ void PrivateToLocalPass::UpdateUse(Instruction* inst) {
"Do not know how to update the type for this instruction.");
break;
}
return true;
}
void PrivateToLocalPass::UpdateUses(uint32_t id) {
bool PrivateToLocalPass::UpdateUses(uint32_t id) {
std::vector<Instruction*> uses;
context()->get_def_use_mgr()->ForEachUser(
id, [&uses](Instruction* use) { uses.push_back(use); });
for (Instruction* use : uses) {
UpdateUse(use);
if (!UpdateUse(use)) {
return false;
}
}
return true;
}
} // namespace opt

View File

@ -41,8 +41,8 @@ class PrivateToLocalPass : public Pass {
private:
// Moves |variable| from the private storage class to the function storage
// class of |function|.
void MoveVariable(Instruction* variable, Function* function);
// class of |function|. Returns false if the variable could not be moved.
bool MoveVariable(Instruction* variable, Function* function);
// |inst| is an instruction declaring a varible. If that variable is
// referenced in a single function and all of uses are valid as defined by
@ -58,13 +58,13 @@ class PrivateToLocalPass : public Pass {
// Given the result id of a pointer type, |old_type_id|, this function
// returns the id of a the same pointer type except the storage class has
// been changed to function. If the type does not already exist, it will be
// created.
// created. Returns 0 if the new type could not be found or generated.
uint32_t GetNewType(uint32_t old_type_id);
// Updates |inst|, and any instruction dependent on |inst|, to reflect the
// change of the base pointer now pointing to the function storage class.
void UpdateUse(Instruction* inst);
void UpdateUses(uint32_t id);
bool UpdateUse(Instruction* inst);
bool UpdateUses(uint32_t id);
};
} // namespace opt

View File

@ -78,36 +78,47 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
}
std::vector<Instruction*> dead;
if (get_def_use_mgr()->WhileEachUser(
inst, [this, &replacements, &dead](Instruction* user) {
if (!IsAnnotationInst(user->opcode())) {
switch (user->opcode()) {
case SpvOpLoad:
ReplaceWholeLoad(user, replacements);
dead.push_back(user);
break;
case SpvOpStore:
ReplaceWholeStore(user, replacements);
dead.push_back(user);
break;
case SpvOpAccessChain:
case SpvOpInBoundsAccessChain:
if (ReplaceAccessChain(user, replacements))
dead.push_back(user);
else
return false;
break;
case SpvOpName:
case SpvOpMemberName:
break;
default:
assert(false && "Unexpected opcode");
break;
bool replaced_all_uses = get_def_use_mgr()->WhileEachUser(
inst, [this, &replacements, &dead](Instruction* user) {
if (!IsAnnotationInst(user->opcode())) {
switch (user->opcode()) {
case SpvOpLoad:
if (ReplaceWholeLoad(user, replacements)) {
dead.push_back(user);
} else {
return false;
}
}
return true;
}))
break;
case SpvOpStore:
if (ReplaceWholeStore(user, replacements)) {
dead.push_back(user);
} else {
return false;
}
break;
case SpvOpAccessChain:
case SpvOpInBoundsAccessChain:
if (ReplaceAccessChain(user, replacements))
dead.push_back(user);
else
return false;
break;
case SpvOpName:
case SpvOpMemberName:
break;
default:
assert(false && "Unexpected opcode");
break;
}
}
return true;
});
if (replaced_all_uses) {
dead.push_back(inst);
} else {
return Status::Failure;
}
// If there are no dead instructions to clean up, return with no changes.
if (dead.empty()) return Status::SuccessWithoutChange;
@ -133,7 +144,7 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
return Status::SuccessWithChange;
}
void ScalarReplacementPass::ReplaceWholeLoad(
bool ScalarReplacementPass::ReplaceWholeLoad(
Instruction* load, const std::vector<Instruction*>& replacements) {
// Replaces the load of the entire composite with a load from each replacement
// variable followed by a composite construction.
@ -150,6 +161,9 @@ void ScalarReplacementPass::ReplaceWholeLoad(
Instruction* type = GetStorageType(var);
uint32_t loadId = TakeNextId();
if (loadId == 0) {
return false;
}
std::unique_ptr<Instruction> newLoad(
new Instruction(context(), SpvOpLoad, type->result_id(), loadId,
std::initializer_list<Operand>{
@ -168,6 +182,9 @@ void ScalarReplacementPass::ReplaceWholeLoad(
// Construct a new composite.
uint32_t compositeId = TakeNextId();
if (compositeId == 0) {
return false;
}
where = load;
std::unique_ptr<Instruction> compositeConstruct(new Instruction(
context(), SpvOpCompositeConstruct, load->type_id(), compositeId, {}));
@ -180,9 +197,10 @@ void ScalarReplacementPass::ReplaceWholeLoad(
get_def_use_mgr()->AnalyzeInstDefUse(&*where);
context()->set_instr_block(&*where, block);
context()->ReplaceAllUsesWith(load->result_id(), compositeId);
return true;
}
void ScalarReplacementPass::ReplaceWholeStore(
bool ScalarReplacementPass::ReplaceWholeStore(
Instruction* store, const std::vector<Instruction*>& replacements) {
// Replaces a store to the whole composite with a series of extract and stores
// to each element.
@ -199,6 +217,9 @@ void ScalarReplacementPass::ReplaceWholeStore(
Instruction* type = GetStorageType(var);
uint32_t extractId = TakeNextId();
if (extractId == 0) {
return false;
}
std::unique_ptr<Instruction> extract(new Instruction(
context(), SpvOpCompositeExtract, type->result_id(), extractId,
std::initializer_list<Operand>{
@ -224,6 +245,7 @@ void ScalarReplacementPass::ReplaceWholeStore(
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
context()->set_instr_block(&*iter, block);
}
return true;
}
bool ScalarReplacementPass::ReplaceAccessChain(
@ -247,6 +269,9 @@ bool ScalarReplacementPass::ReplaceAccessChain(
// Replace input access chain with another access chain.
BasicBlock::iterator chainIter(chain);
uint32_t replacementId = TakeNextId();
if (replacementId == 0) {
return false;
}
std::unique_ptr<Instruction> replacementChain(new Instruction(
context(), chain->opcode(), chain->type_id(), replacementId,
std::initializer_list<Operand>{
@ -329,6 +354,10 @@ void ScalarReplacementPass::TransferAnnotations(
if (decoration == SpvDecorationInvariant ||
decoration == SpvDecorationRestrict) {
for (auto var : *replacements) {
if (var == nullptr) {
continue;
}
std::unique_ptr<Instruction> annotation(
new Instruction(context(), SpvOpDecorate, 0, 0,
std::initializer_list<Operand>{
@ -350,6 +379,11 @@ void ScalarReplacementPass::CreateVariable(
std::vector<Instruction*>* replacements) {
uint32_t ptrId = GetOrCreatePointerType(typeId);
uint32_t id = TakeNextId();
if (id == 0) {
replacements->push_back(nullptr);
}
std::unique_ptr<Instruction> variable(new Instruction(
context(), SpvOpVariable, ptrId, id,
std::initializer_list<Operand>{
@ -671,44 +705,51 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst) const {
bool ScalarReplacementPass::CheckUses(const Instruction* inst,
VariableStats* stats) const {
uint64_t max_legal_index = GetMaxLegalIndex(inst);
bool ok = true;
get_def_use_mgr()->ForEachUse(
inst, [this, stats, &ok](const Instruction* user, uint32_t index) {
// Annotations are check as a group separately.
if (!IsAnnotationInst(user->opcode())) {
switch (user->opcode()) {
case SpvOpAccessChain:
case SpvOpInBoundsAccessChain:
if (index == 2u && user->NumInOperands() > 1) {
uint32_t id = user->GetSingleWordInOperand(1u);
const Instruction* opInst = get_def_use_mgr()->GetDef(id);
if (!IsCompileTimeConstantInst(opInst->opcode())) {
ok = false;
} else {
if (!CheckUsesRelaxed(user)) ok = false;
}
stats->num_partial_accesses++;
} else {
ok = false;
}
break;
case SpvOpLoad:
if (!CheckLoad(user, index)) ok = false;
stats->num_full_accesses++;
break;
case SpvOpStore:
if (!CheckStore(user, index)) ok = false;
stats->num_full_accesses++;
break;
case SpvOpName:
case SpvOpMemberName:
break;
default:
get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok](
const Instruction* user,
uint32_t index) {
// Annotations are check as a group separately.
if (!IsAnnotationInst(user->opcode())) {
switch (user->opcode()) {
case SpvOpAccessChain:
case SpvOpInBoundsAccessChain:
if (index == 2u && user->NumInOperands() > 1) {
uint32_t id = user->GetSingleWordInOperand(1u);
const Instruction* opInst = get_def_use_mgr()->GetDef(id);
const auto* constant =
context()->get_constant_mgr()->GetConstantFromInst(opInst);
if (!constant) {
ok = false;
break;
} else if (constant->GetZeroExtendedValue() >= max_legal_index) {
ok = false;
} else {
if (!CheckUsesRelaxed(user)) ok = false;
}
stats->num_partial_accesses++;
} else {
ok = false;
}
}
});
break;
case SpvOpLoad:
if (!CheckLoad(user, index)) ok = false;
stats->num_full_accesses++;
break;
case SpvOpStore:
if (!CheckStore(user, index)) ok = false;
stats->num_full_accesses++;
break;
case SpvOpName:
case SpvOpMemberName:
break;
default:
ok = false;
break;
}
}
});
return ok;
}
@ -838,5 +879,24 @@ Instruction* ScalarReplacementPass::CreateNullConstant(uint32_t type_id) {
return null_inst;
}
uint64_t ScalarReplacementPass::GetMaxLegalIndex(
const Instruction* var_inst) const {
assert(var_inst->opcode() == SpvOpVariable &&
"|var_inst| must be a variable instruction.");
Instruction* type = GetStorageType(var_inst);
switch (type->opcode()) {
case SpvOpTypeStruct:
return type->NumInOperands();
case SpvOpTypeArray:
return GetArrayLength(type);
case SpvOpTypeMatrix:
case SpvOpTypeVector:
return GetNumElements(type);
default:
return 0;
}
return 0;
}
} // namespace opt
} // namespace spvtools

View File

@ -143,7 +143,8 @@ class ScalarReplacementPass : public Pass {
bool CheckStore(const Instruction* inst, uint32_t index) const;
// Creates a variable of type |typeId| from the |index|'th element of
// |varInst|. The new variable is added to |replacements|.
// |varInst|. The new variable is added to |replacements|. If the variable
// could not be created, then |nullptr| is appended to |replacements|.
void CreateVariable(uint32_t typeId, Instruction* varInst, uint32_t index,
std::vector<Instruction*>* replacements);
@ -187,21 +188,21 @@ class ScalarReplacementPass : public Pass {
// Generates a load for each replacement variable and then creates a new
// composite by combining all of the loads.
//
// |load| must be a load.
void ReplaceWholeLoad(Instruction* load,
// |load| must be a load. Returns true if successful.
bool ReplaceWholeLoad(Instruction* load,
const std::vector<Instruction*>& replacements);
// Replaces the store to the entire composite.
//
// Generates a composite extract and store for each element in the scalarized
// variable from the original store data input.
void ReplaceWholeStore(Instruction* store,
// variable from the original store data input. Returns true if successful.
bool ReplaceWholeStore(Instruction* store,
const std::vector<Instruction*>& replacements);
// Replaces an access chain to the composite variable with either a direct use
// of the appropriate replacement variable or another access chain with the
// replacement variable as the base and one fewer indexes. Returns false if
// the chain has an out of bounds access.
// replacement variable as the base and one fewer indexes. Returns true if
// successful.
bool ReplaceAccessChain(Instruction* chain,
const std::vector<Instruction*>& replacements);
@ -222,10 +223,16 @@ class ScalarReplacementPass : public Pass {
// Maps type id to OpConstantNull for that type.
std::unordered_map<uint32_t, uint32_t> type_to_null_;
// Returns the number of elements in the variable |var_inst|.
uint64_t GetMaxLegalIndex(const Instruction* var_inst) const;
// Returns true if |length| is larger than limit on the size of the variable
// that we will be willing to split.
bool IsLargerThanSizeLimit(uint64_t length) const;
// Limit on the number of members in an object that will be replaced.
// 0 means there is no limit.
uint32_t max_num_elements_;
bool IsLargerThanSizeLimit(uint64_t length) const;
char name_[55];
};

View File

@ -213,6 +213,10 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
std::unique_ptr<Instruction> typeInst;
// TODO(1841): Handle id overflow.
id = context()->TakeNextId();
if (id == 0) {
return 0;
}
RegisterType(id, *type);
switch (type->kind()) {
#define DefineParameterlessCase(kind) \
@ -247,6 +251,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
break;
case Type::kVector: {
uint32_t subtype = GetTypeInstruction(type->AsVector()->element_type());
if (subtype == 0) {
return 0;
}
typeInst =
MakeUnique<Instruction>(context(), SpvOpTypeVector, 0, id,
std::initializer_list<Operand>{
@ -257,6 +264,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
}
case Type::kMatrix: {
uint32_t subtype = GetTypeInstruction(type->AsMatrix()->element_type());
if (subtype == 0) {
return 0;
}
typeInst =
MakeUnique<Instruction>(context(), SpvOpTypeMatrix, 0, id,
std::initializer_list<Operand>{
@ -268,6 +278,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
case Type::kImage: {
const Image* image = type->AsImage();
uint32_t subtype = GetTypeInstruction(image->sampled_type());
if (subtype == 0) {
return 0;
}
typeInst = MakeUnique<Instruction>(
context(), SpvOpTypeImage, 0, id,
std::initializer_list<Operand>{
@ -289,6 +302,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
case Type::kSampledImage: {
uint32_t subtype =
GetTypeInstruction(type->AsSampledImage()->image_type());
if (subtype == 0) {
return 0;
}
typeInst = MakeUnique<Instruction>(
context(), SpvOpTypeSampledImage, 0, id,
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}});
@ -296,6 +312,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
}
case Type::kArray: {
uint32_t subtype = GetTypeInstruction(type->AsArray()->element_type());
if (subtype == 0) {
return 0;
}
typeInst = MakeUnique<Instruction>(
context(), SpvOpTypeArray, 0, id,
std::initializer_list<Operand>{
@ -306,6 +325,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
case Type::kRuntimeArray: {
uint32_t subtype =
GetTypeInstruction(type->AsRuntimeArray()->element_type());
if (subtype == 0) {
return 0;
}
typeInst = MakeUnique<Instruction>(
context(), SpvOpTypeRuntimeArray, 0, id,
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}});
@ -315,7 +337,11 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
std::vector<Operand> ops;
const Struct* structTy = type->AsStruct();
for (auto ty : structTy->element_types()) {
ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {GetTypeInstruction(ty)}));
uint32_t member_type_id = GetTypeInstruction(ty);
if (member_type_id == 0) {
return 0;
}
ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {member_type_id}));
}
typeInst =
MakeUnique<Instruction>(context(), SpvOpTypeStruct, 0, id, ops);
@ -337,6 +363,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
case Type::kPointer: {
const Pointer* pointer = type->AsPointer();
uint32_t subtype = GetTypeInstruction(pointer->pointee_type());
if (subtype == 0) {
return 0;
}
typeInst = MakeUnique<Instruction>(
context(), SpvOpTypePointer, 0, id,
std::initializer_list<Operand>{
@ -348,10 +377,17 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
case Type::kFunction: {
std::vector<Operand> ops;
const Function* function = type->AsFunction();
ops.push_back(Operand(SPV_OPERAND_TYPE_ID,
{GetTypeInstruction(function->return_type())}));
uint32_t return_type_id = GetTypeInstruction(function->return_type());
if (return_type_id == 0) {
return 0;
}
ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {return_type_id}));
for (auto ty : function->param_types()) {
ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {GetTypeInstruction(ty)}));
uint32_t paramater_type_id = GetTypeInstruction(ty);
if (paramater_type_id == 0) {
return 0;
}
ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {paramater_type_id}));
}
typeInst =
MakeUnique<Instruction>(context(), SpvOpTypeFunction, 0, id, ops);
@ -594,6 +630,9 @@ void TypeManager::RegisterType(uint32_t id, const Type& type) {
Type* TypeManager::GetRegisteredType(const Type* type) {
uint32_t id = GetTypeInstruction(type);
if (id == 0) {
return nullptr;
}
return GetType(id);
}

View File

@ -101,7 +101,8 @@ class TypeManager {
std::pair<Type*, std::unique_ptr<Pointer>> GetTypeAndPointerType(
uint32_t id, SpvStorageClass sc) const;
// Returns an id for a declaration representing |type|.
// Returns an id for a declaration representing |type|. Returns 0 if the type
// does not exists, and could not be generated.
//
// If |type| is registered, then the registered id is returned. Otherwise,
// this function recursively adds type and annotation instructions as
@ -109,7 +110,8 @@ class TypeManager {
uint32_t GetTypeInstruction(const Type* type);
// Find pointer to type and storage in module, return its resultId. If it is
// not found, a new type is created, and its id is returned.
// not found, a new type is created, and its id is returned. Returns 0 if the
// type could not be created.
uint32_t FindPointerToType(uint32_t type_id, SpvStorageClass storage_class);
// Registers |id| to |type|.
@ -118,6 +120,7 @@ class TypeManager {
// unchanged.
void RegisterType(uint32_t id, const Type& type);
// Return the registered type object that is the same as |type|.
Type* GetRegisteredType(const Type* type);
// Removes knowledge of |id| from the manager.

View File

@ -571,10 +571,10 @@ void Pointer::GetExtraHashWords(std::vector<uint32_t>* words,
void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; }
Function::Function(Type* ret_type, const std::vector<const Type*>& params)
Function::Function(const Type* ret_type, const std::vector<const Type*>& params)
: Type(kFunction), return_type_(ret_type), param_types_(params) {}
Function::Function(Type* ret_type, std::vector<const Type*>& params)
Function::Function(const Type* ret_type, std::vector<const Type*>& params)
: Type(kFunction), return_type_(ret_type), param_types_(params) {}
bool Function::IsSameImpl(const Type* that, IsSameCache* seen) const {

View File

@ -520,8 +520,8 @@ class Pointer : public Type {
class Function : public Type {
public:
Function(Type* ret_type, const std::vector<const Type*>& params);
Function(Type* ret_type, std::vector<const Type*>& params);
Function(const Type* ret_type, const std::vector<const Type*>& params);
Function(const Type* ret_type, std::vector<const Type*>& params);
Function(const Function&) = default;
std::string str() const override;

View File

@ -0,0 +1,146 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/opt/wrap_opkill.h"
#include "ir_builder.h"
namespace spvtools {
namespace opt {
Pass::Status WrapOpKill::Process() {
bool modified = false;
for (auto& func : *get_module()) {
bool successful = func.WhileEachInst([this, &modified](Instruction* inst) {
if (inst->opcode() == SpvOpKill) {
modified = true;
if (!ReplaceWithFunctionCall(inst)) {
return false;
}
}
return true;
});
if (!successful) {
return Status::Failure;
}
}
if (opkill_function_ != nullptr) {
assert(modified &&
"The function should only be generated if something was modified.");
context()->AddFunction(std::move(opkill_function_));
}
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
}
bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
assert(inst->opcode() == SpvOpKill &&
"|inst| must be an OpKill instruction.");
InstructionBuilder ir_builder(
context(), inst,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
uint32_t func_id = GetOpKillFuncId();
if (func_id == 0) {
return false;
}
if (ir_builder.AddFunctionCall(GetVoidTypeId(), func_id, {}) == nullptr) {
return false;
}
ir_builder.AddUnreachable();
context()->KillInst(inst);
return true;
}
uint32_t WrapOpKill::GetVoidTypeId() {
if (void_type_id_ != 0) {
return void_type_id_;
}
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Void void_type;
void_type_id_ = type_mgr->GetTypeInstruction(&void_type);
return void_type_id_;
}
uint32_t WrapOpKill::GetVoidFunctionTypeId() {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Void void_type;
const analysis::Type* registered_void_type =
type_mgr->GetRegisteredType(&void_type);
analysis::Function func_type(registered_void_type, {});
return type_mgr->GetTypeInstruction(&func_type);
}
uint32_t WrapOpKill::GetOpKillFuncId() {
if (opkill_function_ != nullptr) {
return opkill_function_->result_id();
}
uint32_t opkill_func_id = TakeNextId();
if (opkill_func_id == 0) {
return 0;
}
// Generate the function start instruction
std::unique_ptr<Instruction> func_start(new Instruction(
context(), SpvOpFunction, GetVoidTypeId(), opkill_func_id, {}));
func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}});
func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}});
opkill_function_.reset(new Function(std::move(func_start)));
// Generate the function end instruction
std::unique_ptr<Instruction> func_end(
new Instruction(context(), SpvOpFunctionEnd, 0, 0, {}));
opkill_function_->SetFunctionEnd(std::move(func_end));
// Create the one basic block for the function.
uint32_t lab_id = TakeNextId();
if (lab_id == 0) {
return 0;
}
std::unique_ptr<Instruction> label_inst(
new Instruction(context(), SpvOpLabel, 0, lab_id, {}));
std::unique_ptr<BasicBlock> bb(new BasicBlock(std::move(label_inst)));
// Add the OpKill to the basic block
std::unique_ptr<Instruction> kill_inst(
new Instruction(context(), SpvOpKill, 0, 0, {}));
bb->AddInstruction(std::move(kill_inst));
// Add the bb to the function
opkill_function_->AddBasicBlock(std::move(bb));
// Add the function to the module.
if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) {
opkill_function_->ForEachInst(
[this](Instruction* inst) { context()->AnalyzeDefUse(inst); });
}
if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
for (BasicBlock& basic_block : *opkill_function_) {
context()->set_instr_block(basic_block.GetLabelInst(), &basic_block);
for (Instruction& inst : basic_block) {
context()->set_instr_block(&inst, &basic_block);
}
}
}
return opkill_function_->result_id();
}
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,72 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_OPT_WRAP_OPKILL_H_
#define SOURCE_OPT_WRAP_OPKILL_H_
#include "source/opt/pass.h"
namespace spvtools {
namespace opt {
// Documented in optimizer.hpp
class WrapOpKill : public Pass {
public:
WrapOpKill() : void_type_id_(0) {}
const char* name() const override { return "wrap-opkill"; }
Status Process() override;
IRContext::Analysis GetPreservedAnalyses() override {
return IRContext::kAnalysisDefUse |
IRContext::kAnalysisInstrToBlockMapping |
IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId |
IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants |
IRContext::kAnalysisTypes;
}
private:
// Replaces the OpKill instruction |inst| with a function call to a function
// that contains a single instruction, which is OpKill. An OpUnreachable
// instruction will be placed after the function call. Return true if
// successful.
bool ReplaceWithFunctionCall(Instruction* inst);
// Returns the id of the void type.
uint32_t GetVoidTypeId();
// Returns the id of the function type for a void function with no parameters.
uint32_t GetVoidFunctionTypeId();
// Return the id of a function that has return type void, has no parameters,
// and contains a single instruction, which is an OpKill. Returns 0 if the
// function could not be generated.
uint32_t GetOpKillFuncId();
// The id of the void type. If its value is 0, then the void type has not
// been found or created yet.
uint32_t void_type_id_;
// The function that is a single instruction, which is an OpKill. The
// function has a void return type and takes no parameters. If the function is
// |nullptr|, then the function has not been generated.
std::unique_ptr<Function> opkill_function_;
};
} // namespace opt
} // namespace spvtools
#endif // SOURCE_OPT_WRAP_OPKILL_H_

View File

@ -30,6 +30,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
remove_function_reduction_opportunity.h
remove_function_reduction_opportunity_finder.h
remove_opname_instruction_reduction_opportunity_finder.h
remove_relaxed_precision_decoration_opportunity_finder.h
remove_selection_reduction_opportunity.h
remove_selection_reduction_opportunity_finder.h
remove_unreferenced_instruction_reduction_opportunity_finder.h
@ -56,6 +57,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
remove_function_reduction_opportunity.cpp
remove_function_reduction_opportunity_finder.cpp
remove_instruction_reduction_opportunity.cpp
remove_relaxed_precision_decoration_opportunity_finder.cpp
remove_selection_reduction_opportunity.cpp
remove_selection_reduction_opportunity_finder.cpp
remove_unreferenced_instruction_reduction_opportunity_finder.cpp

View File

@ -25,6 +25,7 @@
#include "source/reduce/remove_block_reduction_opportunity_finder.h"
#include "source/reduce/remove_function_reduction_opportunity_finder.h"
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
#include "source/reduce/remove_selection_reduction_opportunity_finder.h"
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
@ -175,6 +176,8 @@ Reducer::ReductionResultStatus Reducer::Run(
void Reducer::AddDefaultReductionPasses() {
AddReductionPass(spvtools::MakeUnique<
RemoveOpNameInstructionReductionOpportunityFinder>());
AddReductionPass(spvtools::MakeUnique<
RemoveRelaxedPrecisionDecorationOpportunityFinder>());
AddReductionPass(
spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
AddReductionPass(

View File

@ -0,0 +1,49 @@
// Copyright (c) 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
#include "source/reduce/remove_instruction_reduction_opportunity.h"
namespace spvtools {
namespace reduce {
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveRelaxedPrecisionDecorationOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
// Consider all annotation instructions
for (auto& inst : context->module()->annotations()) {
// We are interested in removing instructions of the form:
// SpvOpDecorate %id RelaxedPrecision
// and
// SpvOpMemberDecorate %id member RelaxedPrecision
if ((inst.opcode() == SpvOpDecorate &&
inst.GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) ||
(inst.opcode() == SpvOpMemberDecorate &&
inst.GetSingleWordInOperand(2) == SpvDecorationRelaxedPrecision)) {
result.push_back(
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
}
return result;
}
std::string RemoveRelaxedPrecisionDecorationOpportunityFinder::GetName() const {
return "RemoveRelaxedPrecisionDecorationOpportunityFinder";
}
} // namespace reduce
} // namespace spvtools

View File

@ -0,0 +1,36 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
#include "source/reduce/reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
// A finder for opportunities to remove relaxed precision decorations.
class RemoveRelaxedPrecisionDecorationOpportunityFinder
: public ReductionOpportunityFinder {
public:
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const override;
std::string GetName() const override;
};
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_

View File

@ -21,11 +21,9 @@
namespace spvtools {
namespace reduce {
using opt::IRContext;
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveUnreferencedInstructionReductionOpportunityFinder::
GetAvailableOpportunities(IRContext* context) const {
GetAvailableOpportunities(opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
for (auto& function : *context->module()) {

View File

@ -116,15 +116,13 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _,
inst->GetOperandAs<uint32_t>(constituent_index);
const auto constituent = _.FindDef(constituent_id);
if (!constituent ||
!(SpvOpConstantComposite == constituent->opcode() ||
SpvOpSpecConstantComposite == constituent->opcode() ||
SpvOpUndef == constituent->opcode())) {
!spvOpcodeIsConstantOrUndef(constituent->opcode())) {
// The message says "... or undef" because the spec does not say
// undef is a constant.
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< opcode_name << " Constituent <id> '"
<< _.getIdName(constituent_id)
<< "' is not a constant composite or undef.";
<< "' is not a constant or undef.";
}
const auto vector = _.FindDef(constituent->type_id());
if (!vector) {

View File

@ -923,7 +923,58 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
case OpenCLLIB::Fract:
case OpenCLLIB::Modf:
case OpenCLLIB::Sincos:
case OpenCLLIB::Sincos: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a float scalar or vector type";
}
const uint32_t num_components = _.GetDimension(result_type);
if (num_components > 4 && num_components != 8 && num_components != 16) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a scalar or a vector with 2, "
"3, 4, 8 or 16 components";
}
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
if (result_type != x_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected type of operand X to be equal to Result Type";
}
const uint32_t p_type = _.GetOperandTypeId(inst, 5);
uint32_t p_storage_class = 0;
uint32_t p_data_type = 0;
if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected the last operand to be a pointer";
}
if (p_storage_class != SpvStorageClassGeneric &&
p_storage_class != SpvStorageClassCrossWorkgroup &&
p_storage_class != SpvStorageClassWorkgroup &&
p_storage_class != SpvStorageClassFunction) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected storage class of the pointer to be Generic, "
"CrossWorkgroup, Workgroup or Function";
}
if (result_type != p_data_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected data type of the pointer to be equal to Result "
"Type";
}
break;
}
case OpenCLLIB::Frexp:
case OpenCLLIB::Lgamma_r:
case OpenCLLIB::Remquo: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
@ -975,57 +1026,6 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
"CrossWorkgroup, Workgroup or Function";
}
if (result_type != p_data_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected data type of the pointer to be equal to Result "
"Type";
}
break;
}
case OpenCLLIB::Frexp:
case OpenCLLIB::Lgamma_r: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a float scalar or vector type";
}
const uint32_t num_components = _.GetDimension(result_type);
if (num_components > 4 && num_components != 8 && num_components != 16) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a scalar or a vector with 2, "
"3, 4, 8 or 16 components";
}
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
if (result_type != x_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected type of operand X to be equal to Result Type";
}
const uint32_t p_type = _.GetOperandTypeId(inst, 5);
uint32_t p_storage_class = 0;
uint32_t p_data_type = 0;
if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected the last operand to be a pointer";
}
if (p_storage_class != SpvStorageClassGeneric &&
p_storage_class != SpvStorageClassCrossWorkgroup &&
p_storage_class != SpvStorageClassWorkgroup &&
p_storage_class != SpvStorageClassFunction) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected storage class of the pointer to be Generic, "
"CrossWorkgroup, Workgroup or Function";
}
if (!_.IsIntScalarOrVectorType(p_data_type) ||
_.GetBitWidth(p_data_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)

View File

@ -84,44 +84,46 @@ using TypeMatch = spvtest::LinkerTest;
MatchF(T##Of##A##Of##B, \
MatchPart1(B, b) MatchPart2(A, a, b) MatchPart2(T, type, a))
// clang-format off
// Basic types
Match1(Int);
Match1(Float);
Match1(Opaque);
Match1(Sampler);
Match1(Event);
Match1(DeviceEvent);
Match1(ReserveId);
Match1(Queue);
Match1(Pipe);
Match1(PipeStorage);
Match1(NamedBarrier);
Match1(Int)
Match1(Float)
Match1(Opaque)
Match1(Sampler)
Match1(Event)
Match1(DeviceEvent)
Match1(ReserveId)
Match1(Queue)
Match1(Pipe)
Match1(PipeStorage)
Match1(NamedBarrier)
// Simpler (restricted) compound types
Match2(Vector, Float);
Match3(Matrix, Vector, Float);
Match2(Image, Float);
Match2(Vector, Float)
Match3(Matrix, Vector, Float)
Match2(Image, Float)
// Unrestricted compound types
#define MatchCompounds1(A) \
Match2(RuntimeArray, A); \
Match2(Struct, A); \
Match2(Pointer, A); \
Match2(Function, A); \
Match2(Array, A);
Match2(RuntimeArray, A) \
Match2(Struct, A) \
Match2(Pointer, A) \
Match2(Function, A) \
Match2(Array, A)
#define MatchCompounds2(A, B) \
Match3(RuntimeArray, A, B); \
Match3(Struct, A, B); \
Match3(Pointer, A, B); \
Match3(Function, A, B); \
Match3(Array, A, B);
Match3(RuntimeArray, A, B) \
Match3(Struct, A, B) \
Match3(Pointer, A, B) \
Match3(Function, A, B) \
Match3(Array, A, B)
MatchCompounds1(Float);
MatchCompounds2(Array, Float);
MatchCompounds2(RuntimeArray, Float);
MatchCompounds2(Struct, Float);
MatchCompounds2(Pointer, Float);
MatchCompounds2(Function, Float);
MatchCompounds1(Float)
MatchCompounds2(Array, Float)
MatchCompounds2(RuntimeArray, Float)
MatchCompounds2(Struct, Float)
MatchCompounds2(Pointer, Float)
MatchCompounds2(Function, Float)
// clang-format on
// ForwardPointer tests, which don't fit into the previous mold
#define MatchFpF(N, CODE) \
@ -134,11 +136,13 @@ MatchCompounds2(Function, Float);
#define MatchFp2(T, A) \
MatchFpF(ForwardPointerOf##T, MatchPart1(A, a) MatchPart2(T, realtype, a))
MatchFp1(Float);
MatchFp2(Array, Float);
MatchFp2(RuntimeArray, Float);
MatchFp2(Struct, Float);
MatchFp2(Function, Float);
// clang-format off
MatchFp1(Float)
MatchFp2(Array, Float)
MatchFp2(RuntimeArray, Float)
MatchFp2(Struct, Float)
MatchFp2(Function, Float)
// clang-format on
} // namespace
} // namespace spvtools

View File

@ -52,6 +52,7 @@ add_spvtools_unittest(TARGET opt
inline_test.cpp
insert_extract_elim_test.cpp
inst_bindless_check_test.cpp
inst_buff_addr_check_test.cpp
instruction_list_test.cpp
instruction_test.cpp
ir_builder.cpp
@ -97,6 +98,7 @@ add_spvtools_unittest(TARGET opt
value_table_test.cpp
vector_dce_test.cpp
workaround1209_test.cpp
wrap_opkill_test.cpp
LIBS SPIRV-Tools-opt
PCH_FILE pch_test_opt
)

View File

@ -204,6 +204,67 @@ TEST_F(DescriptorScalarReplacementTest, ExpandSSBO) {
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
}
TEST_F(DescriptorScalarReplacementTest, NameNewVariables) {
// Checks that if the original variable has a name, then the new variables
// will have a name derived from that name.
const std::string text = R"(
; CHECK: OpName [[var1:%\w+]] "SSBO[0]"
; CHECK: OpName [[var2:%\w+]] "SSBO[1]"
; CHECK: OpDecorate [[var1]] DescriptorSet 0
; CHECK: OpDecorate [[var1]] Binding 0
; CHECK: OpDecorate [[var2]] DescriptorSet 0
; CHECK: OpDecorate [[var2]] Binding 1
; CHECK: OpTypeStruct
; CHECK: [[struct_type:%\w+]] = OpTypeStruct
; CHECK: [[ptr_type:%\w+]] = OpTypePointer Uniform [[struct_type]]
; CHECK: [[var1]] = OpVariable [[ptr_type]] Uniform
; CHECK: [[var2]] = OpVariable [[ptr_type]] Uniform
; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var1]] %uint_0 %uint_0 %uint_0
; CHECK: OpLoad %v4float [[ac1]]
; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var2]] %uint_0 %uint_0 %uint_0
; CHECK: OpLoad %v4float [[ac2]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource HLSL 600
OpName %buffers "SSBO"
OpDecorate %buffers DescriptorSet 0
OpDecorate %buffers Binding 0
OpMemberDecorate %S 0 Offset 0
OpDecorate %_runtimearr_S ArrayStride 16
OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0
OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable
OpDecorate %type_StructuredBuffer_S BufferBlock
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%S = OpTypeStruct %v4float
%_runtimearr_S = OpTypeRuntimeArray %S
%type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S
%_arr_type_StructuredBuffer_S_uint_2 = OpTypeArray %type_StructuredBuffer_S %uint_2
%_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 = OpTypePointer Uniform %_arr_type_StructuredBuffer_S_uint_2
%_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S
%void = OpTypeVoid
%19 = OpTypeFunction %void
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%buffers = OpVariable %_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 Uniform
%main = OpFunction %void None %19
%21 = OpLabel
%22 = OpAccessChain %_ptr_Uniform_v4float %buffers %uint_0 %uint_0 %uint_0 %uint_0
%23 = OpLoad %v4float %22
%24 = OpAccessChain %_ptr_Uniform_type_StructuredBuffer_S %buffers %uint_1
%25 = OpAccessChain %_ptr_Uniform_v4float %24 %uint_0 %uint_0 %uint_0
%26 = OpLoad %v4float %25
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
}
} // namespace
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,620 @@
// Copyright (c) 2019 Valve Corporation
// Copyright (c) 2019 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Bindless Check Instrumentation Tests.
// Tests ending with V2 use version 2 record format.
#include <string>
#include <vector>
#include "test/opt/assembly_builder.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using InstBuffAddrTest = PassTest<::testing::Test>;
TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) {
// #version 450
// #extension GL_EXT_buffer_reference : enable
//
// layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;
//
// layout(set = 0, binding = 0) uniform ufoo {
// bufStruct data;
// uint offset;
// } u_info;
//
// layout(buffer_reference, std140) buffer bufStruct {
// layout(offset = 0) int a[2];
// layout(offset = 32) int b;
// };
//
// void main() {
// u_info.data.b = 0xca7;
// }
const std::string defs_before =
R"(OpCapability Shader
OpCapability PhysicalStorageBufferAddressesEXT
OpExtension "SPV_EXT_physical_storage_buffer"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_EXT_buffer_reference"
OpName %main "main"
OpName %ufoo "ufoo"
OpMemberName %ufoo 0 "data"
OpMemberName %ufoo 1 "offset"
OpName %bufStruct "bufStruct"
OpMemberName %bufStruct 0 "a"
OpMemberName %bufStruct 1 "b"
OpName %u_info "u_info"
OpMemberDecorate %ufoo 0 Offset 0
OpMemberDecorate %ufoo 1 Offset 8
OpDecorate %ufoo Block
OpDecorate %_arr_int_uint_2 ArrayStride 16
OpMemberDecorate %bufStruct 0 Offset 0
OpMemberDecorate %bufStruct 1 Offset 32
OpDecorate %bufStruct Block
OpDecorate %u_info DescriptorSet 0
OpDecorate %u_info Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT
%uint = OpTypeInt 32 0
%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint
%int = OpTypeInt 32 1
%uint_2 = OpConstant %uint 2
%_arr_int_uint_2 = OpTypeArray %int %uint_2
%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
%_ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct
%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
%int_0 = OpConstant %int 0
%_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct
%int_1 = OpConstant %int 1
%int_3239 = OpConstant %int 3239
%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
)";
const std::string defs_after =
R"(OpCapability Shader
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpExtension "SPV_EXT_physical_storage_buffer"
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_EXT_buffer_reference"
OpName %main "main"
OpName %ufoo "ufoo"
OpMemberName %ufoo 0 "data"
OpMemberName %ufoo 1 "offset"
OpName %bufStruct "bufStruct"
OpMemberName %bufStruct 0 "a"
OpMemberName %bufStruct 1 "b"
OpName %u_info "u_info"
OpMemberDecorate %ufoo 0 Offset 0
OpMemberDecorate %ufoo 1 Offset 8
OpDecorate %ufoo Block
OpDecorate %_arr_int_uint_2 ArrayStride 16
OpMemberDecorate %bufStruct 0 Offset 0
OpMemberDecorate %bufStruct 1 Offset 32
OpDecorate %bufStruct Block
OpDecorate %u_info DescriptorSet 0
OpDecorate %u_info Binding 0
OpDecorate %_runtimearr_ulong ArrayStride 8
OpDecorate %_struct_39 Block
OpMemberDecorate %_struct_39 0 Offset 0
OpDecorate %41 DescriptorSet 7
OpDecorate %41 Binding 2
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_77 Block
OpMemberDecorate %_struct_77 0 Offset 0
OpMemberDecorate %_struct_77 1 Offset 4
OpDecorate %79 DescriptorSet 7
OpDecorate %79 Binding 0
OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
%void = OpTypeVoid
%8 = OpTypeFunction %void
OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT
%uint = OpTypeInt 32 0
%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint
%int = OpTypeInt 32 1
%uint_2 = OpConstant %uint 2
%_arr_int_uint_2 = OpTypeArray %int %uint_2
%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
%_ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct
%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
%int_0 = OpConstant %int 0
%_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct
%int_1 = OpConstant %int 1
%int_3239 = OpConstant %int 3239
%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
%ulong = OpTypeInt 64 0
%uint_4 = OpConstant %uint 4
%bool = OpTypeBool
%28 = OpTypeFunction %bool %ulong %uint
%uint_1 = OpConstant %uint 1
%_runtimearr_ulong = OpTypeRuntimeArray %ulong
%_struct_39 = OpTypeStruct %_runtimearr_ulong
%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
%uint_0 = OpConstant %uint 0
%uint_32 = OpConstant %uint 32
%70 = OpTypeFunction %void %uint %uint %uint %uint
%_runtimearr_uint = OpTypeRuntimeArray %uint
%_struct_77 = OpTypeStruct %uint %_runtimearr_uint
%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77
%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
%uint_10 = OpConstant %uint 10
%uint_23 = OpConstant %uint 23
%uint_5 = OpConstant %uint 5
%uint_3 = OpConstant %uint 3
%v3uint = OpTypeVector %uint 3
%_ptr_Input_v3uint = OpTypePointer Input %v3uint
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
%uint_6 = OpConstant %uint 6
%uint_7 = OpConstant %uint 7
%uint_8 = OpConstant %uint 8
%uint_9 = OpConstant %uint 9
%uint_48 = OpConstant %uint 48
)";
const std::string func_before =
R"(%main = OpFunction %void None %3
%5 = OpLabel
%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0
%18 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %17
%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %18 %int_1
OpStore %22 %int_3239 Aligned 16
OpReturn
OpFunctionEnd
)";
const std::string func_after =
R"(%main = OpFunction %void None %8
%19 = OpLabel
%20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0
%21 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %20
%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %21 %int_1
%24 = OpConvertPtrToU %ulong %22
%61 = OpFunctionCall %bool %26 %24 %uint_4
OpSelectionMerge %62 None
OpBranchConditional %61 %63 %64
%63 = OpLabel
OpStore %22 %int_3239 Aligned 16
OpBranch %62
%64 = OpLabel
%65 = OpUConvert %uint %24
%67 = OpShiftRightLogical %ulong %24 %uint_32
%68 = OpUConvert %uint %67
%124 = OpFunctionCall %void %69 %uint_48 %uint_2 %65 %68
OpBranch %62
%62 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string new_funcs =
R"(%26 = OpFunction %bool None %28
%29 = OpFunctionParameter %ulong
%30 = OpFunctionParameter %uint
%31 = OpLabel
OpBranch %32
%32 = OpLabel
%34 = OpPhi %uint %uint_1 %31 %35 %33
OpLoopMerge %37 %33 None
OpBranch %33
%33 = OpLabel
%35 = OpIAdd %uint %34 %uint_1
%44 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %35
%45 = OpLoad %ulong %44
%46 = OpUGreaterThan %bool %45 %29
OpBranchConditional %46 %37 %32
%37 = OpLabel
%47 = OpISub %uint %35 %uint_1
%48 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %47
%49 = OpLoad %ulong %48
%50 = OpISub %ulong %29 %49
%51 = OpUConvert %ulong %30
%52 = OpIAdd %ulong %50 %51
%53 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %uint_0
%54 = OpLoad %ulong %53
%55 = OpUConvert %uint %54
%56 = OpISub %uint %47 %uint_1
%57 = OpIAdd %uint %56 %55
%58 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %57
%59 = OpLoad %ulong %58
%60 = OpULessThanEqual %bool %52 %59
OpReturnValue %60
OpFunctionEnd
%69 = OpFunction %void None %70
%71 = OpFunctionParameter %uint
%72 = OpFunctionParameter %uint
%73 = OpFunctionParameter %uint
%74 = OpFunctionParameter %uint
%75 = OpLabel
%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0
%83 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10
%84 = OpIAdd %uint %83 %uint_10
%85 = OpArrayLength %uint %79 1
%86 = OpULessThanEqual %bool %84 %85
OpSelectionMerge %87 None
OpBranchConditional %86 %88 %87
%88 = OpLabel
%89 = OpIAdd %uint %83 %uint_0
%90 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %89
OpStore %90 %uint_10
%92 = OpIAdd %uint %83 %uint_1
%93 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %92
OpStore %93 %uint_23
%94 = OpIAdd %uint %83 %uint_2
%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94
OpStore %95 %71
%98 = OpIAdd %uint %83 %uint_3
%99 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %98
OpStore %99 %uint_5
%103 = OpLoad %v3uint %gl_GlobalInvocationID
%104 = OpCompositeExtract %uint %103 0
%105 = OpCompositeExtract %uint %103 1
%106 = OpCompositeExtract %uint %103 2
%107 = OpIAdd %uint %83 %uint_4
%108 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %107
OpStore %108 %104
%109 = OpIAdd %uint %83 %uint_5
%110 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %109
OpStore %110 %105
%112 = OpIAdd %uint %83 %uint_6
%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112
OpStore %113 %106
%115 = OpIAdd %uint %83 %uint_7
%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115
OpStore %116 %72
%118 = OpIAdd %uint %83 %uint_8
%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118
OpStore %119 %73
%121 = OpIAdd %uint %83 %uint_9
%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121
OpStore %122 %74
OpBranch %87
%87 = OpLabel
OpReturn
OpFunctionEnd
)";
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBuffAddrCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
true, 7u, 23u, 2u);
}
TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
// #version 450
// #extension GL_EXT_buffer_reference : enable
// // forward reference
// layout(buffer_reference) buffer blockType;
// layout(buffer_reference, std430, buffer_reference_align = 16) buffer
// blockType {
// int x;
// blockType next;
// };
// layout(std430) buffer rootBlock {
// blockType root;
// } r;
// void main()
// {
// blockType b = r.root;
// b = b.next;
// b.x = 531;
// }
const std::string defs_before =
R"(OpCapability Shader
OpCapability PhysicalStorageBufferAddressesEXT
OpExtension "SPV_EXT_physical_storage_buffer"
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_EXT_buffer_reference"
OpName %main "main"
OpName %blockType "blockType"
OpMemberName %blockType 0 "x"
OpMemberName %blockType 1 "next"
OpName %rootBlock "rootBlock"
OpMemberName %rootBlock 0 "root"
OpName %r "r"
OpMemberDecorate %blockType 0 Offset 0
OpMemberDecorate %blockType 1 Offset 8
OpDecorate %blockType Block
OpMemberDecorate %rootBlock 0 Offset 0
OpDecorate %rootBlock Block
OpDecorate %r DescriptorSet 0
OpDecorate %r Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
%int = OpTypeInt 32 1
%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
%int_0 = OpConstant %int 0
%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
%int_1 = OpConstant %int 1
%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
%int_531 = OpConstant %int 531
%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
)";
const std::string defs_after =
R"(OpCapability Shader
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Int64
OpExtension "SPV_EXT_physical_storage_buffer"
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_EXT_buffer_reference"
OpName %main "main"
OpName %blockType "blockType"
OpMemberName %blockType 0 "x"
OpMemberName %blockType 1 "next"
OpName %rootBlock "rootBlock"
OpMemberName %rootBlock 0 "root"
OpName %r "r"
OpMemberDecorate %blockType 0 Offset 0
OpMemberDecorate %blockType 1 Offset 8
OpDecorate %blockType Block
OpMemberDecorate %rootBlock 0 Offset 0
OpDecorate %rootBlock Block
OpDecorate %r DescriptorSet 0
OpDecorate %r Binding 0
OpDecorate %_runtimearr_ulong ArrayStride 8
OpDecorate %_struct_45 Block
OpMemberDecorate %_struct_45 0 Offset 0
OpDecorate %47 DescriptorSet 7
OpDecorate %47 Binding 2
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_84 Block
OpMemberDecorate %_struct_84 0 Offset 0
OpMemberDecorate %_struct_84 1 Offset 4
OpDecorate %86 DescriptorSet 7
OpDecorate %86 Binding 0
OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
%void = OpTypeVoid
%3 = OpTypeFunction %void
OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
%int = OpTypeInt 32 1
%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
%int_0 = OpConstant %int 0
%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
%int_1 = OpConstant %int 1
%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
%int_531 = OpConstant %int 531
%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%ulong = OpTypeInt 64 0
%uint_8 = OpConstant %uint 8
%bool = OpTypeBool
%34 = OpTypeFunction %bool %ulong %uint
%uint_1 = OpConstant %uint 1
%_runtimearr_ulong = OpTypeRuntimeArray %ulong
%_struct_45 = OpTypeStruct %_runtimearr_ulong
%_ptr_StorageBuffer__struct_45 = OpTypePointer StorageBuffer %_struct_45
%47 = OpVariable %_ptr_StorageBuffer__struct_45 StorageBuffer
%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
%uint_0 = OpConstant %uint 0
%uint_32 = OpConstant %uint 32
%77 = OpTypeFunction %void %uint %uint %uint %uint
%_runtimearr_uint = OpTypeRuntimeArray %uint
%_struct_84 = OpTypeStruct %uint %_runtimearr_uint
%_ptr_StorageBuffer__struct_84 = OpTypePointer StorageBuffer %_struct_84
%86 = OpVariable %_ptr_StorageBuffer__struct_84 StorageBuffer
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
%uint_10 = OpConstant %uint 10
%uint_4 = OpConstant %uint 4
%uint_23 = OpConstant %uint 23
%uint_5 = OpConstant %uint 5
%uint_3 = OpConstant %uint 3
%v3uint = OpTypeVector %uint 3
%_ptr_Input_v3uint = OpTypePointer Input %v3uint
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
%uint_6 = OpConstant %uint 6
%uint_7 = OpConstant %uint 7
%uint_9 = OpConstant %uint 9
%uint_44 = OpConstant %uint 44
%132 = OpConstantNull %_ptr_PhysicalStorageBufferEXT_blockType
%uint_46 = OpConstant %uint 46
)";
const std::string func_before =
R"(%main = OpFunction %void None %3
%5 = OpLabel
%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
%22 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %22 %int_0
OpStore %26 %int_531 Aligned 16
OpReturn
OpFunctionEnd
)";
const std::string func_after =
R"(%main = OpFunction %void None %3
%5 = OpLabel
%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
%30 = OpConvertPtrToU %ulong %21
%67 = OpFunctionCall %bool %32 %30 %uint_8
OpSelectionMerge %68 None
OpBranchConditional %67 %69 %70
%69 = OpLabel
%71 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
OpBranch %68
%70 = OpLabel
%72 = OpUConvert %uint %30
%74 = OpShiftRightLogical %ulong %30 %uint_32
%75 = OpUConvert %uint %74
%131 = OpFunctionCall %void %76 %uint_44 %uint_2 %72 %75
OpBranch %68
%68 = OpLabel
%133 = OpPhi %_ptr_PhysicalStorageBufferEXT_blockType %71 %69 %132 %70
%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %133 %int_0
%134 = OpConvertPtrToU %ulong %26
%135 = OpFunctionCall %bool %32 %134 %uint_4
OpSelectionMerge %136 None
OpBranchConditional %135 %137 %138
%137 = OpLabel
OpStore %26 %int_531 Aligned 16
OpBranch %136
%138 = OpLabel
%139 = OpUConvert %uint %134
%140 = OpShiftRightLogical %ulong %134 %uint_32
%141 = OpUConvert %uint %140
%143 = OpFunctionCall %void %76 %uint_46 %uint_2 %139 %141
OpBranch %136
%136 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string new_funcs =
R"(%32 = OpFunction %bool None %34
%35 = OpFunctionParameter %ulong
%36 = OpFunctionParameter %uint
%37 = OpLabel
OpBranch %38
%38 = OpLabel
%40 = OpPhi %uint %uint_1 %37 %41 %39
OpLoopMerge %43 %39 None
OpBranch %39
%39 = OpLabel
%41 = OpIAdd %uint %40 %uint_1
%50 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %41
%51 = OpLoad %ulong %50
%52 = OpUGreaterThan %bool %51 %35
OpBranchConditional %52 %43 %38
%43 = OpLabel
%53 = OpISub %uint %41 %uint_1
%54 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %53
%55 = OpLoad %ulong %54
%56 = OpISub %ulong %35 %55
%57 = OpUConvert %ulong %36
%58 = OpIAdd %ulong %56 %57
%59 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %uint_0
%60 = OpLoad %ulong %59
%61 = OpUConvert %uint %60
%62 = OpISub %uint %53 %uint_1
%63 = OpIAdd %uint %62 %61
%64 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %63
%65 = OpLoad %ulong %64
%66 = OpULessThanEqual %bool %58 %65
OpReturnValue %66
OpFunctionEnd
%76 = OpFunction %void None %77
%78 = OpFunctionParameter %uint
%79 = OpFunctionParameter %uint
%80 = OpFunctionParameter %uint
%81 = OpFunctionParameter %uint
%82 = OpLabel
%88 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_0
%91 = OpAtomicIAdd %uint %88 %uint_4 %uint_0 %uint_10
%92 = OpIAdd %uint %91 %uint_10
%93 = OpArrayLength %uint %86 1
%94 = OpULessThanEqual %bool %92 %93
OpSelectionMerge %95 None
OpBranchConditional %94 %96 %95
%96 = OpLabel
%97 = OpIAdd %uint %91 %uint_0
%98 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %97
OpStore %98 %uint_10
%100 = OpIAdd %uint %91 %uint_1
%101 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %100
OpStore %101 %uint_23
%102 = OpIAdd %uint %91 %uint_2
%103 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %102
OpStore %103 %78
%106 = OpIAdd %uint %91 %uint_3
%107 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %106
OpStore %107 %uint_5
%111 = OpLoad %v3uint %gl_GlobalInvocationID
%112 = OpCompositeExtract %uint %111 0
%113 = OpCompositeExtract %uint %111 1
%114 = OpCompositeExtract %uint %111 2
%115 = OpIAdd %uint %91 %uint_4
%116 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %115
OpStore %116 %112
%117 = OpIAdd %uint %91 %uint_5
%118 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %117
OpStore %118 %113
%120 = OpIAdd %uint %91 %uint_6
%121 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %120
OpStore %121 %114
%123 = OpIAdd %uint %91 %uint_7
%124 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %123
OpStore %124 %79
%125 = OpIAdd %uint %91 %uint_8
%126 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %125
OpStore %126 %80
%128 = OpIAdd %uint %91 %uint_9
%129 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %128
OpStore %129 %81
OpBranch %95
%95 = OpLabel
OpReturn
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBuffAddrCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
true, 7u, 23u, 2u);
}
} // namespace
} // namespace opt
} // namespace spvtools

View File

@ -75,7 +75,9 @@ class PassTest : public TestT {
const auto status = pass->Run(context());
std::vector<uint32_t> binary;
context()->module()->ToBinary(&binary, skip_nop);
if (status != Pass::Status::Failure) {
context()->module()->ToBinary(&binary, skip_nop);
}
return std::make_tuple(binary, status);
}
@ -241,15 +243,18 @@ class PassTest : public TestT {
context()->set_preserve_spec_constants(
OptimizerOptions()->preserve_spec_constants_);
manager_->Run(context());
auto status = manager_->Run(context());
EXPECT_NE(status, Pass::Status::Failure);
std::vector<uint32_t> binary;
context()->module()->ToBinary(&binary, /* skip_nop = */ false);
if (status != Pass::Status::Failure) {
std::vector<uint32_t> binary;
context()->module()->ToBinary(&binary, /* skip_nop = */ false);
std::string optimized;
SpirvTools tools(env_);
EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_));
EXPECT_EQ(expected, optimized);
std::string optimized;
SpirvTools tools(env_);
EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_));
EXPECT_EQ(expected, optimized);
}
}
void SetAssembleOptions(uint32_t assemble_options) {

View File

@ -419,6 +419,39 @@ OpFunctionEnd
SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
}
TEST_F(PrivateToLocalTest, IdBoundOverflow1) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginLowerLeft
OpSource HLSL 84
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypeStruct %7
%4194302 = OpTypeStruct %8 %8
%9 = OpTypeStruct %8 %8
%11 = OpTypePointer Private %7
%18 = OpTypeStruct %6 %9
%12 = OpVariable %11 Private
%4 = OpFunction %2 None %3
%5 = OpLabel
%13 = OpLoad %7 %12
OpReturn
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
std::vector<Message> messages = {
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
SetMessageConsumer(GetTestMessageConsumer(messages));
auto result = SinglePassRunToBinary<PrivateToLocalPass>(text, true);
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
}
} // namespace
} // namespace opt
} // namespace spvtools

View File

@ -1621,7 +1621,7 @@ TEST_F(ScalarReplacementTest, TestAccessChainWithNoIndexes) {
}
// Test that id overflow is handled gracefully.
TEST_F(ScalarReplacementTest, IdBoundOverflow) {
TEST_F(ScalarReplacementTest, IdBoundOverflow1) {
const std::string text = R"(
OpCapability ImageQuery
OpMemoryModel Logical GLSL450
@ -1652,8 +1652,103 @@ OpFunctionEnd
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
SetMessageConsumer(GetTestMessageConsumer(messages));
auto result =
SinglePassRunAndDisassemble<ScalarReplacementPass>(text, true, false);
auto result = SinglePassRunToBinary<ScalarReplacementPass>(text, true, false);
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
}
// Test that id overflow is handled gracefully.
TEST_F(ScalarReplacementTest, IdBoundOverflow2) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %17
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypeStruct %7
%9 = OpTypePointer Function %8
%16 = OpTypePointer Output %7
%21 = OpTypeInt 32 1
%22 = OpConstant %21 0
%23 = OpTypePointer Function %7
%17 = OpVariable %16 Output
%4 = OpFunction %2 None %3
%5 = OpLabel
%4194300 = OpVariable %23 Function
%10 = OpVariable %9 Function
%4194301 = OpAccessChain %23 %10 %22
%4194302 = OpLoad %7 %4194301
OpStore %4194300 %4194302
%15 = OpLoad %7 %4194300
OpStore %17 %15
OpReturn
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
std::vector<Message> messages = {
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
SetMessageConsumer(GetTestMessageConsumer(messages));
auto result = SinglePassRunToBinary<ScalarReplacementPass>(text, true, false);
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
}
// Test that id overflow is handled gracefully.
TEST_F(ScalarReplacementTest, IdBoundOverflow3) {
const std::string text = R"(
OpCapability InterpolationFunction
OpExtension "z"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeStruct %6 %6
%9 = OpTypePointer Function %7
%18 = OpTypeFunction %7 %9
%21 = OpTypeInt 32 0
%22 = OpConstant %21 4293000676
%4194302 = OpConstantNull %6
%4 = OpFunction %2 Inline|Pure %3
%786464 = OpLabel
%4194298 = OpVariable %9 Function
%10 = OpVariable %9 Function
%4194299 = OpUDiv %21 %22 %22
%4194300 = OpLoad %7 %10
%50959 = OpLoad %7 %4194298
OpKill
OpFunctionEnd
%1 = OpFunction %7 None %18
%19 = OpFunctionParameter %9
%147667 = OpLabel
%2044391 = OpUDiv %21 %22 %22
%25 = OpLoad %7 %19
OpReturnValue %25
OpFunctionEnd
%4194295 = OpFunction %2 None %3
%4194296 = OpLabel
OpKill
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
std::vector<Message> messages = {
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
SetMessageConsumer(GetTestMessageConsumer(messages));
auto result = SinglePassRunToBinary<ScalarReplacementPass>(text, true, false);
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
}

View File

@ -65,17 +65,18 @@ class SameTypeTest : public ::testing::Test {
#define TestMultipleInstancesOfTheSameType(ty, ...) \
TestMultipleInstancesOfTheSameTypeQualified(ty, Simple, __VA_ARGS__)
TestMultipleInstancesOfTheSameType(Void);
TestMultipleInstancesOfTheSameType(Bool);
TestMultipleInstancesOfTheSameType(Integer, 32, true);
TestMultipleInstancesOfTheSameType(Float, 64);
TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3);
TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4);
// clang-format off
TestMultipleInstancesOfTheSameType(Void)
TestMultipleInstancesOfTheSameType(Bool)
TestMultipleInstancesOfTheSameType(Integer, 32, true)
TestMultipleInstancesOfTheSameType(Float, 64)
TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3)
TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4)
TestMultipleInstancesOfTheSameType(Image, f64_t_.get(), SpvDimCube, 0, 0, 1, 1,
SpvImageFormatRgb10A2,
SpvAccessQualifierWriteOnly);
TestMultipleInstancesOfTheSameType(Sampler);
TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get());
SpvAccessQualifierWriteOnly)
TestMultipleInstancesOfTheSameType(Sampler)
TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get())
// There are three classes of arrays, based on the kinds of length information
// they have.
// 1. Array length is a constant or spec constant without spec ID, with literals
@ -85,34 +86,35 @@ TestMultipleInstancesOfTheSameTypeQualified(Array, LenConstant, u32_t_.get(),
{
0,
9999,
}});
}})
// 2. Array length is a spec constant with a given spec id.
TestMultipleInstancesOfTheSameTypeQualified(Array, LenSpecId, u32_t_.get(),
Array::LengthInfo{42, {1, 99}});
Array::LengthInfo{42, {1, 99}})
// 3. Array length is an OpSpecConstantOp expression
TestMultipleInstancesOfTheSameTypeQualified(Array, LenDefiningId, u32_t_.get(),
Array::LengthInfo{42, {2, 42}});
Array::LengthInfo{42, {2, 42}})
TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get());
TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get())
TestMultipleInstancesOfTheSameType(Struct, std::vector<const Type*>{
u32_t_.get(), f64_t_.get()});
TestMultipleInstancesOfTheSameType(Opaque, "testing rocks");
TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), SpvStorageClassInput);
u32_t_.get(), f64_t_.get()})
TestMultipleInstancesOfTheSameType(Opaque, "testing rocks")
TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), SpvStorageClassInput)
TestMultipleInstancesOfTheSameType(Function, u32_t_.get(),
{f64_t_.get(), f64_t_.get()});
TestMultipleInstancesOfTheSameType(Event);
TestMultipleInstancesOfTheSameType(DeviceEvent);
TestMultipleInstancesOfTheSameType(ReserveId);
TestMultipleInstancesOfTheSameType(Queue);
TestMultipleInstancesOfTheSameType(Pipe, SpvAccessQualifierReadWrite);
TestMultipleInstancesOfTheSameType(ForwardPointer, 10, SpvStorageClassUniform);
TestMultipleInstancesOfTheSameType(PipeStorage);
TestMultipleInstancesOfTheSameType(NamedBarrier);
TestMultipleInstancesOfTheSameType(AccelerationStructureNV);
{f64_t_.get(), f64_t_.get()})
TestMultipleInstancesOfTheSameType(Event)
TestMultipleInstancesOfTheSameType(DeviceEvent)
TestMultipleInstancesOfTheSameType(ReserveId)
TestMultipleInstancesOfTheSameType(Queue)
TestMultipleInstancesOfTheSameType(Pipe, SpvAccessQualifierReadWrite)
TestMultipleInstancesOfTheSameType(ForwardPointer, 10, SpvStorageClassUniform)
TestMultipleInstancesOfTheSameType(PipeStorage)
TestMultipleInstancesOfTheSameType(NamedBarrier)
TestMultipleInstancesOfTheSameType(AccelerationStructureNV)
#undef TestMultipleInstanceOfTheSameType
#undef TestMultipleInstanceOfTheSameTypeQual
std::vector<std::unique_ptr<Type>> GenerateAllTypes() {
// clang-format on
// Types in this test case are only equal to themselves, nothing else.
std::vector<std::unique_ptr<Type>> types;

View File

@ -0,0 +1,267 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gmock/gmock.h"
#include "test/opt/assembly_builder.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using WrapOpKillTest = PassTest<::testing::Test>;
TEST_F(WrapOpKillTest, SingleOpKill) {
const std::string text = R"(
; CHECK: OpEntryPoint Fragment [[main:%\w+]]
; CHECK: [[main]] = OpFunction
; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
; CHECK: [[orig_kill]] = OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
; CHECK-NEXT: OpUnreachable
; CHECK: [[new_kill]] = OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpKill
; CHECK-NEXT: OpFunctionEnd
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
%void = OpTypeVoid
%5 = OpTypeFunction %void
%bool = OpTypeBool
%true = OpConstantTrue %bool
%main = OpFunction %void None %5
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %10 %11 None
OpBranch %12
%12 = OpLabel
OpBranchConditional %true %13 %10
%13 = OpLabel
OpBranch %11
%11 = OpLabel
%14 = OpFunctionCall %void %kill_
OpBranch %9
%10 = OpLabel
OpReturn
OpFunctionEnd
%kill_ = OpFunction %void None %5
%15 = OpLabel
OpKill
OpFunctionEnd
)";
SinglePassRunAndMatch<WrapOpKill>(text, true);
}
TEST_F(WrapOpKillTest, MultipleOpKillInSameFunc) {
const std::string text = R"(
; CHECK: OpEntryPoint Fragment [[main:%\w+]]
; CHECK: [[main]] = OpFunction
; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
; CHECK: [[orig_kill]] = OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpBranchConditional
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
; CHECK-NEXT: OpUnreachable
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
; CHECK-NEXT: OpUnreachable
; CHECK: [[new_kill]] = OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpKill
; CHECK-NEXT: OpFunctionEnd
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
%void = OpTypeVoid
%5 = OpTypeFunction %void
%bool = OpTypeBool
%true = OpConstantTrue %bool
%main = OpFunction %void None %5
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %10 %11 None
OpBranch %12
%12 = OpLabel
OpBranchConditional %true %13 %10
%13 = OpLabel
OpBranch %11
%11 = OpLabel
%14 = OpFunctionCall %void %kill_
OpBranch %9
%10 = OpLabel
OpReturn
OpFunctionEnd
%kill_ = OpFunction %void None %5
%15 = OpLabel
OpSelectionMerge %16 None
OpBranchConditional %true %17 %18
%17 = OpLabel
OpKill
%18 = OpLabel
OpKill
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<WrapOpKill>(text, true);
}
TEST_F(WrapOpKillTest, MultipleOpKillInDifferentFunc) {
const std::string text = R"(
; CHECK: OpEntryPoint Fragment [[main:%\w+]]
; CHECK: [[main]] = OpFunction
; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]]
; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]]
; CHECK: [[orig_kill1]] = OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
; CHECK-NEXT: OpUnreachable
; CHECK: [[orig_kill2]] = OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
; CHECK-NEXT: OpUnreachable
; CHECK: [[new_kill]] = OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpKill
; CHECK-NEXT: OpFunctionEnd
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%bool = OpTypeBool
%true = OpConstantTrue %bool
%main = OpFunction %void None %4
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpLoopMerge %9 %10 None
OpBranch %11
%11 = OpLabel
OpBranchConditional %true %12 %9
%12 = OpLabel
OpBranch %10
%10 = OpLabel
%13 = OpFunctionCall %void %14
%15 = OpFunctionCall %void %16
OpBranch %8
%9 = OpLabel
OpReturn
OpFunctionEnd
%14 = OpFunction %void None %4
%17 = OpLabel
OpKill
OpFunctionEnd
%16 = OpFunction %void None %4
%18 = OpLabel
OpKill
OpFunctionEnd
)";
SinglePassRunAndMatch<WrapOpKill>(text, true);
}
TEST_F(WrapOpKillTest, IdBoundOverflow1) {
const std::string text = R"(
OpCapability GeometryStreams
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 Pure|Const %3
%4194302 = OpLabel
OpKill
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
std::vector<Message> messages = {
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
SetMessageConsumer(GetTestMessageConsumer(messages));
auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
}
TEST_F(WrapOpKillTest, IdBoundOverflow2) {
const std::string text = R"(
OpCapability GeometryStreams
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 Pure|Const %3
%4194301 = OpLabel
OpKill
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
std::vector<Message> messages = {
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
SetMessageConsumer(GetTestMessageConsumer(messages));
auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
}
TEST_F(WrapOpKillTest, IdBoundOverflow3) {
const std::string text = R"(
OpCapability GeometryStreams
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 Pure|Const %3
%4194300 = OpLabel
OpKill
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
std::vector<Message> messages = {
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
SetMessageConsumer(GetTestMessageConsumer(messages));
auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
}
} // namespace
} // namespace opt
} // namespace spvtools

View File

@ -24,6 +24,7 @@ add_spvtools_unittest(TARGET reduce
remove_block_test.cpp
remove_function_test.cpp
remove_opname_instruction_test.cpp
remove_relaxed_precision_decoration_test.cpp
remove_selection_test.cpp
remove_unreferenced_instruction_test.cpp
structured_loop_to_selection_test.cpp

View File

@ -0,0 +1,177 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
#include "source/opt/build_module.h"
#include "source/reduce/reduction_opportunity.h"
#include "source/reduce/reduction_pass.h"
#include "test/reduce/reduce_test_util.h"
namespace spvtools {
namespace reduce {
namespace {
TEST(RemoveRelaxedPrecisionDecorationTest, NothingToRemove) {
const std::string source = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, source, kReduceAssembleOption);
const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(0, ops.size());
}
TEST(RemoveRelaxedPrecisionDecorationTest, RemoveDecorations) {
const std::string source = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "f"
OpName %12 "i"
OpName %16 "v"
OpName %19 "S"
OpMemberName %19 0 "a"
OpMemberName %19 1 "b"
OpMemberName %19 2 "c"
OpName %21 "s"
OpDecorate %8 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %16 RelaxedPrecision
OpDecorate %17 RelaxedPrecision
OpDecorate %18 RelaxedPrecision
OpMemberDecorate %19 0 RelaxedPrecision
OpMemberDecorate %19 1 RelaxedPrecision
OpMemberDecorate %19 2 RelaxedPrecision
OpDecorate %22 RelaxedPrecision
OpDecorate %23 RelaxedPrecision
OpDecorate %24 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%9 = OpConstant %6 2
%10 = OpTypeInt 32 1
%11 = OpTypePointer Function %10
%13 = OpConstant %10 22
%14 = OpTypeVector %6 2
%15 = OpTypePointer Function %14
%19 = OpTypeStruct %10 %6 %14
%20 = OpTypePointer Function %19
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%12 = OpVariable %11 Function
%16 = OpVariable %15 Function
%21 = OpVariable %20 Function
OpStore %8 %9
OpStore %12 %13
%17 = OpLoad %6 %8
%18 = OpCompositeConstruct %14 %17 %17
OpStore %16 %18
%22 = OpLoad %10 %12
%23 = OpLoad %6 %8
%24 = OpLoad %14 %16
%25 = OpCompositeConstruct %19 %22 %23 %24
OpStore %21 %25
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, source, kReduceAssembleOption);
const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(11, ops.size());
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
}
const std::string expected = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "f"
OpName %12 "i"
OpName %16 "v"
OpName %19 "S"
OpMemberName %19 0 "a"
OpMemberName %19 1 "b"
OpMemberName %19 2 "c"
OpName %21 "s"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%9 = OpConstant %6 2
%10 = OpTypeInt 32 1
%11 = OpTypePointer Function %10
%13 = OpConstant %10 22
%14 = OpTypeVector %6 2
%15 = OpTypePointer Function %14
%19 = OpTypeStruct %10 %6 %14
%20 = OpTypePointer Function %19
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%12 = OpVariable %11 Function
%16 = OpVariable %15 Function
%21 = OpVariable %20 Function
OpStore %8 %9
OpStore %12 %13
%17 = OpLoad %6 %8
%18 = OpCompositeConstruct %14 %17 %17
OpStore %16 %18
%22 = OpLoad %10 %12
%23 = OpLoad %6 %8
%24 = OpLoad %14 %16
%25 = OpCompositeConstruct %19 %22 %23 %24
OpStore %21 %25
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
} // namespace
} // namespace reduce
} // namespace spvtools

View File

@ -57,7 +57,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4,
"""Tests that spirv-opt accepts all valid optimization flags."""
flags = [
'--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids',
'--wrap-opkill', '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids',
'--convert-local-access-chains', '--copy-propagate-arrays',
'--eliminate-dead-branches',
'--eliminate-dead-code-aggressive', '--eliminate-dead-const',
@ -76,6 +76,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4,
'--unify-const'
]
expected_passes = [
'wrap-opkill',
'ccp',
'cfg-cleanup',
'combine-access-chains',
@ -134,6 +135,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_4,
flags = ['-O']
expected_passes = [
'wrap-opkill',
'eliminate-dead-branches',
'merge-return',
'inline-entry-points-exhaustive',
@ -181,6 +183,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_4,
flags = ['-Os']
expected_passes = [
'wrap-opkill',
'eliminate-dead-branches',
'merge-return',
'inline-entry-points-exhaustive',
@ -221,6 +224,7 @@ class TestLegalizationPasses(expect.ValidObjectFile1_4,
flags = ['--legalize-hlsl']
expected_passes = [
'wrap-opkill',
'eliminate-dead-branches',
'merge-return',
'inline-entry-points-exhaustive',

View File

@ -442,6 +442,22 @@ OpMemoryModel Logical GLSL450
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}
TEST_F(ValidateConstant, NullMatrix) {
std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%mat2x2 = OpTypeMatrix %v2float 2
%null_vector = OpConstantNull %v2float
%null_matrix = OpConstantComposite %mat2x2 %null_vector %null_vector
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@ -5315,10 +5315,10 @@ INSTANTIATE_TEST_SUITE_P(AllFractLike, ValidateOpenCLStdFractLike,
TEST_F(ValidateExtInst, OpenCLStdRemquoSuccess) {
const std::string body = R"(
%var_f32 = OpVariable %f32_ptr_function Function
%var_f32vec2 = OpVariable %f32vec2_ptr_function Function
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_f32
%val2 = OpExtInst %f32vec2 %extinst remquo %f32vec2_01 %f32vec2_12 %var_f32vec2
%var_u32 = OpVariable %u32_ptr_function Function
%var_u32vec2 = OpVariable %u32vec2_ptr_function Function
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32
%val2 = OpExtInst %f32vec2 %extinst remquo %f32vec2_01 %f32vec2_12 %var_u32vec2
)";
CompileSuccessfully(GenerateKernelCode(body));
@ -5327,8 +5327,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoSuccess) {
TEST_F(ValidateExtInst, OpenCLStdRemquoIntResultType) {
const std::string body = R"(
%var_f32 = OpVariable %f32_ptr_function Function
%val1 = OpExtInst %u32 %extinst remquo %f32_3 %f32_2 %var_f32
%var_u32 = OpVariable %u32_ptr_function Function
%val1 = OpExtInst %u32 %extinst remquo %f32_3 %f32_2 %var_u32
)";
CompileSuccessfully(GenerateKernelCode(body));
@ -5341,8 +5341,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoIntResultType) {
TEST_F(ValidateExtInst, OpenCLStdRemquoXWrongType) {
const std::string body = R"(
%var_f32 = OpVariable %f32_ptr_function Function
%val1 = OpExtInst %f32 %extinst remquo %u32_3 %f32_2 %var_f32
%var_u32 = OpVariable %f32_ptr_function Function
%val1 = OpExtInst %f32 %extinst remquo %u32_3 %f32_2 %var_u32
)";
CompileSuccessfully(GenerateKernelCode(body));
@ -5355,8 +5355,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoXWrongType) {
TEST_F(ValidateExtInst, OpenCLStdRemquoYWrongType) {
const std::string body = R"(
%var_f32 = OpVariable %f32_ptr_function Function
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %u32_2 %var_f32
%var_u32 = OpVariable %f32_ptr_function Function
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %u32_2 %var_u32
)";
CompileSuccessfully(GenerateKernelCode(body));
@ -5395,17 +5395,44 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongStorageClass) {
TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataType) {
const std::string body = R"(
%var_u32 = OpVariable %u32_ptr_function Function
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32
%var_f32 = OpVariable %f32_ptr_function Function
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_f32
)";
CompileSuccessfully(GenerateKernelCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpenCL.std remquo: "
"expected data type of the pointer to be a 32-bit int "
"scalar or vector type"));
}
TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataTypeWidth) {
const std::string body = R"(
%var_u64 = OpVariable %u64_ptr_function Function
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u64
)";
CompileSuccessfully(GenerateKernelCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpenCL.std remquo: "
"expected data type of the pointer to be a 32-bit int "
"scalar or vector type"));
}
TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongNumberOfComponents) {
const std::string body = R"(
%var_u32vec2 = OpVariable %u32vec2_ptr_function Function
%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32vec2
)";
CompileSuccessfully(GenerateKernelCode(body));
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpenCL.std remquo: "
"expected data type of the pointer to be equal to Result Type"));
HasSubstr("OpenCL.std remquo: "
"expected data type of the pointer to have the same number "
"of components as Result Type"));
}
TEST_P(ValidateOpenCLStdFrexpLike, Success) {

View File

@ -1715,7 +1715,7 @@ TEST_F(ValidateIdWithMessage,
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '7[%7]' is "
"not a constant composite or undef."));
"not a constant or undef."));
}
// Invalid: Composite contains a column that is *not* a vector (it's an array)