Allow pointers to pointers in logical addressing mode.

A few optimizations are updates to handle code that is suppose to be
using the logical addressing mode, but still has variables that contain
pointers as long as the pointer are to opaque objects.  This is called
"relaxed logical addressing".

|Instruction::GetBaseAddress| will check that pointers that are use meet
the relaxed logical addressing rules.  Optimization that now handle
relaxed logical addressing instead of logical addressing are:

 - aggressive dead-code elimination
 - local access chain convert
 - local store elimination passes.
This commit is contained in:
Steven Perron 2017-12-11 13:10:24 -05:00
parent b86eb6842b
commit 79a00649b4
17 changed files with 637 additions and 37 deletions

View File

@ -271,7 +271,8 @@ Optimizer::PassToken CreateInlineOpaquePass();
// The presence of access chain references and function calls can inhibit
// the above optimization.
//
// Only modules with logical addressing are currently processed.
// Only modules with relaxed logical addressing (see opt/instruction.h) are
// currently processed.
//
// This pass is most effective if preceeded by Inlining and
// LocalAccessChainConvert. This pass will reduce the work needed to be done
@ -305,9 +306,9 @@ Optimizer::PassToken CreateDeadBranchElimPass();
// The presence of access chain references and function calls can inhibit
// the above optimization.
//
// Only shader modules with logical addressing are currently processed.
// Currently modules with any extensions enabled are not processed. This
// is left for future work.
// Only shader modules with relaxed logical addressing (see opt/instruction.h)
// are currently processed. Currently modules with any extensions enabled are
// not processed. This is left for future work.
//
// This pass is most effective if preceeded by Inlining and
// LocalAccessChainConvert. LocalSingleStoreElim and LocalSingleBlockElim
@ -344,6 +345,9 @@ Optimizer::PassToken CreateLocalAccessChainConvertPass();
// not supported and will prohibit optimization of a function. Support of
// these operations are future work.
//
// Only shader modules with relaxed logical addressing (see opt/instruction.h)
// are currently processed.
//
// This pass will reduce the work needed to be done by LocalSingleBlockElim
// and LocalMultiStoreElim and can improve the effectiveness of other passes
// such as DeadBranchElimination which depend on values for their analysis.
@ -384,8 +388,8 @@ Optimizer::PassToken CreateCommonUniformElimPass();
// time cost over standard dead code elimination.
//
// This pass only processes entry point functions. It also only processes
// shaders with logical addressing. It currently will not process functions
// with function calls.
// shaders with relaxed logical addressing (see opt/instruction.h). It currently
// will not process functions with function calls.
//
// This pass will be made more effective by first running passes that remove
// dead control flow and inlines function calls.

View File

@ -417,3 +417,23 @@ bool spvOpcodeIsBlockTerminator(SpvOp opcode) {
return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturn(opcode) ||
opcode == SpvOpKill || opcode == SpvOpUnreachable;
}
bool spvOpcodeIsBaseOpaqueType(SpvOp opcode) {
switch (opcode) {
case SpvOpTypeImage:
case SpvOpTypeSampler:
case SpvOpTypeSampledImage:
case SpvOpTypeOpaque:
case SpvOpTypeEvent:
case SpvOpTypeDeviceEvent:
case SpvOpTypeReserveId:
case SpvOpTypeQueue:
case SpvOpTypePipe:
case SpvOpTypeForwardPointer:
case SpvOpTypePipeStorage:
case SpvOpTypeNamedBarrier:
return true;
default:
return false;
}
}

View File

@ -106,4 +106,6 @@ bool spvOpcodeIsReturn(SpvOp opcode);
// Returns true if the given opcode is a basic block terminator.
bool spvOpcodeIsBlockTerminator(SpvOp opcode);
// Returns true if the given opcode always defines an opaque type.
bool spvOpcodeIsBaseOpaqueType(SpvOp opcode);
#endif // LIBSPIRV_OPCODE_H_

View File

@ -36,6 +36,7 @@ const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
} // namespace
bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) {
if (varId == 0) return false;
const ir::Instruction* varInst = get_def_use_mgr()->GetDef(varId);
const SpvOp op = varInst->opcode();
if (op != SpvOpVariable) return false;
@ -355,7 +356,9 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
if (liveInst->opcode() == SpvOpLoad) {
uint32_t varId;
(void)GetPtr(liveInst, &varId);
ProcessLoad(varId);
if (varId != 0) {
ProcessLoad(varId);
}
}
// If function call, treat as if it loads from all pointer arguments
else if (liveInst->opcode() == SpvOpFunctionCall) {
@ -466,7 +469,8 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
// TODO(greg-lunarg): Handle additional capabilities
if (!get_module()->HasCapability(SpvCapabilityShader))
return Status::SuccessWithoutChange;
// Current functionality assumes logical addressing only
// Current functionality assumes relaxed logical addressing (see
// instruction.h)
// TODO(greg-lunarg): Handle non-logical addressing
if (get_module()->HasCapability(SpvCapabilityAddresses))
return Status::SuccessWithoutChange;

View File

@ -49,7 +49,8 @@ class AggressiveDCEPass : public MemPass {
}
private:
// Return true if |varId| is variable of |storageClass|.
// Return true if |varId| is a variable of |storageClass|. |varId| must either
// be 0 or the result of an instruction.
bool IsVarOfStorage(uint32_t varId, uint32_t storageClass);
// Return true if |varId| is variable of function storage class or is

View File

@ -156,8 +156,11 @@ bool Instruction::IsReadOnlyLoad() const {
}
Instruction* Instruction::GetBaseAddress() const {
assert(IsLoad() &&
"GetBaseAddress should only be called on load instructions.");
assert((IsLoad() || opcode() == SpvOpStore || opcode() == SpvOpAccessChain ||
opcode() == SpvOpInBoundsAccessChain ||
opcode() == SpvOpCopyObject) &&
"GetBaseAddress should only be called on instructions that take a "
"pointer or image.");
uint32_t base = GetSingleWordInOperand(kLoadBaseIndex);
ir::Instruction* base_inst = context()->get_def_use_mgr()->GetDef(base);
bool done = false;
@ -179,6 +182,9 @@ Instruction* Instruction::GetBaseAddress() const {
break;
}
}
assert(base_inst->IsValidBasePointer() &&
"We cannot have a base pointer come from this load");
return base_inst;
}
@ -389,5 +395,54 @@ Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
i.get()->InsertBefore(this);
return i.release();
}
bool Instruction::IsValidBasePointer() const {
uint32_t tid = type_id();
if (tid == 0) {
return false;
}
ir::Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
if (type->opcode() != SpvOpTypePointer) {
return false;
}
if (context()->module()->HasCapability(SpvCapabilityAddresses)) {
// TODO: The rules here could be more restrictive.
return true;
}
if (opcode() == SpvOpVariable || opcode() == SpvOpFunctionParameter) {
return true;
}
uint32_t pointee_type_id = type->GetSingleWordInOperand(1);
ir::Instruction* pointee_type_inst =
context()->get_def_use_mgr()->GetDef(pointee_type_id);
if (pointee_type_inst->IsOpaqueType()) {
return true;
}
return false;
}
bool Instruction::IsOpaqueType() const {
if (opcode() == SpvOpTypeStruct) {
bool is_opaque = false;
ForEachInOperand([&is_opaque, this](const uint32_t* op_id) {
ir::Instruction* type_inst = context()->get_def_use_mgr()->GetDef(*op_id);
is_opaque |= type_inst->IsOpaqueType();
});
return is_opaque;
} else if (opcode() == SpvOpTypeArray) {
uint32_t sub_type_id = GetSingleWordInOperand(0);
ir::Instruction* sub_type_inst =
context()->get_def_use_mgr()->GetDef(sub_type_id);
return sub_type_inst->IsOpaqueType();
} else {
return opcode() == SpvOpTypeRuntimeArray ||
spvOpcodeIsBaseOpaqueType(opcode());
}
}
} // namespace ir
} // namespace spvtools

View File

@ -35,6 +35,19 @@ class IRContext;
class Module;
class InstructionList;
// Relaxed logcial addressing:
//
// In the logical addressing model, pointers cannot be stored or loaded. This
// is a useful assumption because it simplifies the aliasing significantly.
// However, for the purpose of legalizing code generated from HLSL, we will have
// to allow storing and loading of pointers to opaque objects and runtime
// arrays. This relaxation of the rule still implies that function and private
// scope variables do not have any aliasing, so we can treat them as before.
// This will be call the relaxed logical addressing model.
//
// This relaxation of the rule will be allowed by |GetBaseAddress|, but it will
// enforce that no other pointers are stored or loaded.
// About operand:
//
// In the SPIR-V specification, the term "operand" is used to mean any single
@ -257,8 +270,9 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// Returns the instruction that gives the base address of an address
// calculation. The instruction must be a load instruction. In logical
// addressing mode, will return an OpVariable or OpFunctionParameter
// instruction. For physical addressing mode, could return other types of
// instructions.
// instruction. For relaxed logical addressing, it would also return a load of
// a pointer to an opaque object. For physical addressing mode, could return
// other types of instructions.
Instruction* GetBaseAddress() const;
// Returns true if the instruction is a load from memory into a result id. It
@ -312,6 +326,11 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
return spvOpcodeIsBlockTerminator(opcode());
}
// Return true if |this| is an instruction that define an opaque type. Since
// runtime array have similar characteristics they are included as opaque
// types.
bool IsOpaqueType() const;
inline bool operator==(const Instruction&) const;
inline bool operator!=(const Instruction&) const;
inline bool operator<(const Instruction&) const;
@ -332,6 +351,12 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
bool IsReadOnlyVariableShaders() const;
bool IsReadOnlyVariableKernel() const;
// Returns true if it is valid to use the result of |inst| as the base
// pointer for a load or store. In this case, valid is defined by the relaxed
// logical addressing rules when using logical addressing. Normal validation
// rules for physical addressing.
bool IsValidBasePointer() const;
IRContext* context_; // IR Context
SpvOp opcode_; // Opcode
uint32_t type_id_; // Result type id. A value of 0 means no result type id.

View File

@ -188,7 +188,7 @@ bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const {
}
Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
// Assumes logical addressing only
// Assumes relaxed logical addressing only (see instruction.h).
if (get_module()->HasCapability(SpvCapabilityAddresses))
return Status::SuccessWithoutChange;
// Do not process if module contains OpGroupDecorate. Additional

View File

@ -307,7 +307,7 @@ bool LocalSingleStoreElimPass::AllExtensionsSupported() const {
}
Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
// Assumes logical addressing only
// Assumes relaxed logical addressing only (see instruction.h)
if (get_module()->HasCapability(SpvCapabilityAddresses))
return Status::SuccessWithoutChange;
// Do not process if module contains OpGroupDecorate. Additional

View File

@ -79,7 +79,7 @@ Pass::Status LocalMultiStoreElimPass::ProcessImpl() {
// TODO(greg-lunarg): Do SSA rewrite for non-structured control flow
if (!get_module()->HasCapability(SpvCapabilityShader))
return Status::SuccessWithoutChange;
// Assumes logical addressing only
// Assumes relaxed logical addressing only (see instruction.h)
// TODO(greg-lunarg): Add support for physical addressing
if (get_module()->HasCapability(SpvCapabilityAddresses))
return Status::SuccessWithoutChange;

View File

@ -26,7 +26,6 @@ namespace opt {
namespace {
const uint32_t kAccessChainPtrIdInIdx = 0;
const uint32_t kCopyObjectOperandInIdx = 0;
const uint32_t kLoadPtrIdInIdx = 0;
const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
@ -48,6 +47,7 @@ bool MemPass::IsBaseTargetType(const ir::Instruction* typeInst) const {
case SpvOpTypeImage:
case SpvOpTypeSampler:
case SpvOpTypeSampledImage:
case SpvOpTypePointer:
return true;
default:
break;
@ -57,9 +57,13 @@ bool MemPass::IsBaseTargetType(const ir::Instruction* typeInst) const {
bool MemPass::IsTargetType(const ir::Instruction* typeInst) const {
if (IsBaseTargetType(typeInst)) return true;
if (typeInst->opcode() == SpvOpTypeArray)
return IsBaseTargetType(
get_def_use_mgr()->GetDef(typeInst->GetSingleWordOperand(1)));
if (typeInst->opcode() == SpvOpTypeArray) {
if (!IsTargetType(
get_def_use_mgr()->GetDef(typeInst->GetSingleWordOperand(1)))) {
return false;
}
return true;
}
if (typeInst->opcode() != SpvOpTypeStruct) return false;
// All struct members must be math type
int nonMathComp = 0;
@ -92,21 +96,25 @@ bool MemPass::IsPtr(uint32_t ptrId) {
ir::Instruction* MemPass::GetPtr(uint32_t ptrId, uint32_t* varId) {
*varId = ptrId;
ir::Instruction* ptrInst = get_def_use_mgr()->GetDef(*varId);
ir::Instruction* varInst;
if (ptrInst->opcode() != SpvOpVariable &&
ptrInst->opcode() != SpvOpFunctionParameter) {
varInst = ptrInst->GetBaseAddress();
} else {
varInst = ptrInst;
}
if (varInst->opcode() == SpvOpVariable) {
*varId = varInst->result_id();
} else {
*varId = 0;
}
while (ptrInst->opcode() == SpvOpCopyObject) {
*varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
ptrInst = get_def_use_mgr()->GetDef(*varId);
}
ir::Instruction* varInst = ptrInst;
while (varInst->opcode() != SpvOpVariable &&
varInst->opcode() != SpvOpFunctionParameter) {
if (IsNonPtrAccessChain(varInst->opcode())) {
*varId = varInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
} else {
assert(varInst->opcode() == SpvOpCopyObject);
*varId = varInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
}
varInst = get_def_use_mgr()->GetDef(*varId);
uint32_t temp = ptrInst->GetSingleWordInOperand(0);
ptrInst = get_def_use_mgr()->GetDef(temp);
}
return ptrInst;
}
@ -170,6 +178,11 @@ bool MemPass::IsLiveStore(ir::Instruction* storeInst) {
// get store's variable
uint32_t varId;
(void)GetPtr(storeInst, &varId);
if (varId == 0) {
// If we do not know which variable we are accessing, assume the store is
// live.
return true;
}
return IsLiveVar(varId);
}
@ -490,12 +503,15 @@ void MemPass::SSABlockInit(std::list<ir::BasicBlock*>::iterator block_itr) {
}
bool MemPass::IsTargetVar(uint32_t varId) {
if (varId == 0) {
return false;
}
if (seen_non_target_vars_.find(varId) != seen_non_target_vars_.end())
return false;
if (seen_target_vars_.find(varId) != seen_target_vars_.end()) return true;
const ir::Instruction* varInst = get_def_use_mgr()->GetDef(varId);
if (varInst->opcode() != SpvOpVariable) return false;
;
const uint32_t varTypeId = varInst->type_id();
const ir::Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=

View File

@ -58,11 +58,13 @@ class MemPass : public Pass {
bool IsPtr(uint32_t ptrId);
// Given the id of a pointer |ptrId|, return the top-most non-CopyObj.
// Also return the base variable's id in |varId|.
// Also return the base variable's id in |varId|. If no base variable is
// found, |varId| will be 0.
ir::Instruction* GetPtr(uint32_t ptrId, uint32_t* varId);
// Given a load or store |ip|, return the pointer instruction.
// Also return the base variable's id in |varId|.
// Also return the base variable's id in |varId|. If no base variable is
// found, |varId| will be 0.
ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* varId);
// Return true if all uses of |id| are only name or decorate ops.
@ -89,7 +91,7 @@ class MemPass : public Pass {
// useless. If a load is deleted and its variable has no other loads,
// delete all its variable's stores.
void DCEInst(ir::Instruction* inst,
const std::function<void(ir::Instruction * )>&);
const std::function<void(ir::Instruction*)>&);
// Call all the cleanup helper functions on |func|.
bool CFGCleanup(ir::Function* func);

View File

@ -3500,6 +3500,113 @@ OpFunctionEnd
SinglePassRunAndCheck<opt::AggressiveDCEPass>(assembly, assembly, true, true);
}
TEST_F(AggressiveDCETest, PointerVariable) {
// ADCE is able to handle code that contains a load whose base address
// comes from a load and not an OpVariable. I want to see an instruction
// removed to be sure that ADCE is not exiting early.
const std::string before =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %_struct_3 0 Offset 0
OpDecorate %_runtimearr__struct_3 ArrayStride 16
OpMemberDecorate %_struct_5 0 Offset 0
OpDecorate %_struct_5 BufferBlock
OpMemberDecorate %_struct_6 0 Offset 0
OpDecorate %_struct_6 BufferBlock
OpDecorate %2 Location 0
OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
OpDecorate %8 DescriptorSet 0
OpDecorate %8 Binding 1
%void = OpTypeVoid
%10 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_struct_3 = OpTypeStruct %v4float
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
%_struct_6 = OpTypeStruct %int
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5
%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6
%int_0 = OpConstant %int 0
%uint_0 = OpConstant %uint 0
%2 = OpVariable %_ptr_Output_v4float Output
%7 = OpVariable %_ptr_Uniform__struct_5 Uniform
%8 = OpVariable %_ptr_Uniform__struct_6 Uniform
%1 = OpFunction %void None %10
%23 = OpLabel
%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function
OpStore %24 %7
%26 = OpLoad %_ptr_Uniform__struct_5 %24
%27 = OpAccessChain %_ptr_Uniform_v4float %26 %int_0 %uint_0 %int_0
%28 = OpLoad %v4float %27
%29 = OpCopyObject %v4float %28
OpStore %2 %28
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %_struct_3 0 Offset 0
OpDecorate %_runtimearr__struct_3 ArrayStride 16
OpMemberDecorate %_struct_5 0 Offset 0
OpDecorate %_struct_5 BufferBlock
OpMemberDecorate %_struct_6 0 Offset 0
OpDecorate %_struct_6 BufferBlock
OpDecorate %2 Location 0
OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
OpDecorate %8 DescriptorSet 0
OpDecorate %8 Binding 1
%void = OpTypeVoid
%10 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_struct_3 = OpTypeStruct %v4float
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
%_struct_6 = OpTypeStruct %int
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5
%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6
%int_0 = OpConstant %int 0
%uint_0 = OpConstant %uint 0
%2 = OpVariable %_ptr_Output_v4float Output
%7 = OpVariable %_ptr_Uniform__struct_5 Uniform
%8 = OpVariable %_ptr_Uniform__struct_6 Uniform
%1 = OpFunction %void None %10
%23 = OpLabel
%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function
OpStore %24 %7
%25 = OpLoad %_ptr_Uniform__struct_5 %24
%26 = OpAccessChain %_ptr_Uniform_v4float %25 %int_0 %uint_0 %int_0
%27 = OpLoad %v4float %26
OpStore %2 %27
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::AggressiveDCEPass>(before, after, true, true);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Check that logical addressing required

View File

@ -31,6 +31,7 @@ using ir::Operand;
using spvtest::MakeInstruction;
using ::testing::Eq;
using DescriptorTypeTest = PassTest<::testing::Test>;
using OpaqueTypeTest = PassTest<::testing::Test>;
TEST(InstructionTest, CreateTrivial) {
Instruction empty;
@ -509,4 +510,71 @@ TEST_F(DescriptorTypeTest, NonWritableIsReadOnly) {
Instruction* variable = context->get_def_use_mgr()->GetDef(3);
EXPECT_TRUE(variable->IsReadOnlyVariable());
}
TEST_F(OpaqueTypeTest, BaseOpaqueTypesShader) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 430
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeFloat 32
%6 = OpTypeImage %5 2D 1 0 0 1 Unknown
%7 = OpTypeSampler
%8 = OpTypeSampledImage %6
%9 = OpTypeRuntimeArray %5
%2 = OpFunction %3 None %4
%10 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
Instruction* image_type = context->get_def_use_mgr()->GetDef(6);
EXPECT_TRUE(image_type->IsOpaqueType());
Instruction* sampler_type = context->get_def_use_mgr()->GetDef(7);
EXPECT_TRUE(sampler_type->IsOpaqueType());
Instruction* sampled_image_type = context->get_def_use_mgr()->GetDef(8);
EXPECT_TRUE(sampled_image_type->IsOpaqueType());
Instruction* runtime_array_type = context->get_def_use_mgr()->GetDef(9);
EXPECT_TRUE(runtime_array_type->IsOpaqueType());
Instruction* float_type = context->get_def_use_mgr()->GetDef(5);
EXPECT_FALSE(float_type->IsOpaqueType());
Instruction* void_type = context->get_def_use_mgr()->GetDef(3);
EXPECT_FALSE(void_type->IsOpaqueType());
}
TEST_F(OpaqueTypeTest, OpaqueStructTypes) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 430
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeFloat 32
%6 = OpTypeRuntimeArray %5
%7 = OpTypeStruct %6 %6
%8 = OpTypeStruct %5 %6
%9 = OpTypeStruct %6 %5
%10 = OpTypeStruct %7
%2 = OpFunction %3 None %4
%11 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
for (int i = 7; i <= 10; i++) {
Instruction* type = context->get_def_use_mgr()->GetDef(i);
EXPECT_TRUE(type->IsOpaqueType());
}
}
} // anonymous namespace

View File

@ -819,6 +819,104 @@ OpFunctionEnd
predefs_before + before, predefs_after + after, true, true);
}
TEST_F(LocalSingleBlockLoadStoreElimTest, PointerVariable) {
// Test that checks if a pointer variable is removed.
const std::string before =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %_struct_3 0 Offset 0
OpDecorate %_runtimearr__struct_3 ArrayStride 16
OpMemberDecorate %_struct_5 0 Offset 0
OpDecorate %_struct_5 BufferBlock
OpMemberDecorate %_struct_6 0 Offset 0
OpDecorate %_struct_6 BufferBlock
OpDecorate %2 Location 0
OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_struct_3 = OpTypeStruct %v4float
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
%_struct_6 = OpTypeStruct %int
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5
%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6
%int_0 = OpConstant %int 0
%uint_0 = OpConstant %uint 0
%2 = OpVariable %_ptr_Output_v4float Output
%7 = OpVariable %_ptr_Uniform__struct_5 Uniform
%1 = OpFunction %void None %10
%23 = OpLabel
%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function
OpStore %24 %7
%26 = OpLoad %_ptr_Uniform__struct_5 %24
%27 = OpAccessChain %_ptr_Uniform_v4float %26 %int_0 %uint_0 %int_0
%28 = OpLoad %v4float %27
%29 = OpCopyObject %v4float %28
OpStore %2 %28
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %_struct_3 0 Offset 0
OpDecorate %_runtimearr__struct_3 ArrayStride 16
OpMemberDecorate %_struct_5 0 Offset 0
OpDecorate %_struct_5 BufferBlock
OpMemberDecorate %_struct_6 0 Offset 0
OpDecorate %_struct_6 BufferBlock
OpDecorate %2 Location 0
OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_struct_3 = OpTypeStruct %v4float
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
%_struct_6 = OpTypeStruct %int
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5
%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6
%int_0 = OpConstant %int 0
%uint_0 = OpConstant %uint 0
%2 = OpVariable %_ptr_Output_v4float Output
%7 = OpVariable %_ptr_Uniform__struct_5 Uniform
%1 = OpFunction %void None %10
%23 = OpLabel
%27 = OpAccessChain %_ptr_Uniform_v4float %7 %int_0 %uint_0 %int_0
%28 = OpLoad %v4float %27
%29 = OpCopyObject %v4float %28
OpStore %2 %28
OpReturn
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<opt::LocalSingleBlockLoadStoreElimPass>(before, after,
true, true);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Other target variable types

View File

@ -722,6 +722,105 @@ OpFunctionEnd
predefs_before + before, predefs_after + after, true, true);
}
TEST_F(LocalSingleStoreElimTest, PointerVariable) {
// Test that checks if a pointer variable is removed.
const std::string before =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %_struct_3 0 Offset 0
OpDecorate %_runtimearr__struct_3 ArrayStride 16
OpMemberDecorate %_struct_5 0 Offset 0
OpDecorate %_struct_5 BufferBlock
OpMemberDecorate %_struct_6 0 Offset 0
OpDecorate %_struct_6 BufferBlock
OpDecorate %2 Location 0
OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_struct_3 = OpTypeStruct %v4float
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
%_struct_6 = OpTypeStruct %int
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5
%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6
%int_0 = OpConstant %int 0
%uint_0 = OpConstant %uint 0
%2 = OpVariable %_ptr_Output_v4float Output
%7 = OpVariable %_ptr_Uniform__struct_5 Uniform
%1 = OpFunction %void None %10
%23 = OpLabel
%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function
OpStore %24 %7
%26 = OpLoad %_ptr_Uniform__struct_5 %24
%27 = OpAccessChain %_ptr_Uniform_v4float %26 %int_0 %uint_0 %int_0
%28 = OpLoad %v4float %27
%29 = OpCopyObject %v4float %28
OpStore %2 %28
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %_struct_3 0 Offset 0
OpDecorate %_runtimearr__struct_3 ArrayStride 16
OpMemberDecorate %_struct_5 0 Offset 0
OpDecorate %_struct_5 BufferBlock
OpMemberDecorate %_struct_6 0 Offset 0
OpDecorate %_struct_6 BufferBlock
OpDecorate %2 Location 0
OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_struct_3 = OpTypeStruct %v4float
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
%_struct_6 = OpTypeStruct %int
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5
%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6
%int_0 = OpConstant %int 0
%uint_0 = OpConstant %uint 0
%2 = OpVariable %_ptr_Output_v4float Output
%7 = OpVariable %_ptr_Uniform__struct_5 Uniform
%1 = OpFunction %void None %10
%23 = OpLabel
%27 = OpAccessChain %_ptr_Uniform_v4float %7 %int_0 %uint_0 %int_0
%28 = OpLoad %v4float %27
%29 = OpCopyObject %v4float %28
OpStore %2 %28
OpReturn
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<opt::LocalSingleStoreElimPass>(before, after, true,
true);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Other types

View File

@ -1513,6 +1513,105 @@ OpFunctionEnd
predefs_before + func_before, predefs_after + func_after, true, true);
}
TEST_F(LocalSSAElimTest, PointerVariable) {
// Test that checks if a pointer variable is removed.
const std::string before =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %_struct_3 0 Offset 0
OpDecorate %_runtimearr__struct_3 ArrayStride 16
OpMemberDecorate %_struct_5 0 Offset 0
OpDecorate %_struct_5 BufferBlock
OpMemberDecorate %_struct_6 0 Offset 0
OpDecorate %_struct_6 BufferBlock
OpDecorate %2 Location 0
OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_struct_3 = OpTypeStruct %v4float
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
%_struct_6 = OpTypeStruct %int
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5
%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6
%int_0 = OpConstant %int 0
%uint_0 = OpConstant %uint 0
%2 = OpVariable %_ptr_Output_v4float Output
%7 = OpVariable %_ptr_Uniform__struct_5 Uniform
%1 = OpFunction %void None %10
%23 = OpLabel
%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function
OpStore %24 %7
%26 = OpLoad %_ptr_Uniform__struct_5 %24
%27 = OpAccessChain %_ptr_Uniform_v4float %26 %int_0 %uint_0 %int_0
%28 = OpLoad %v4float %27
%29 = OpCopyObject %v4float %28
OpStore %2 %28
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main" %2
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %_struct_3 0 Offset 0
OpDecorate %_runtimearr__struct_3 ArrayStride 16
OpMemberDecorate %_struct_5 0 Offset 0
OpDecorate %_struct_5 BufferBlock
OpMemberDecorate %_struct_6 0 Offset 0
OpDecorate %_struct_6 BufferBlock
OpDecorate %2 Location 0
OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_struct_3 = OpTypeStruct %v4float
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
%_struct_6 = OpTypeStruct %int
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5
%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6
%int_0 = OpConstant %int 0
%uint_0 = OpConstant %uint 0
%2 = OpVariable %_ptr_Output_v4float Output
%7 = OpVariable %_ptr_Uniform__struct_5 Uniform
%1 = OpFunction %void None %10
%23 = OpLabel
%27 = OpAccessChain %_ptr_Uniform_v4float %7 %int_0 %uint_0 %int_0
%28 = OpLoad %v4float %27
%29 = OpCopyObject %v4float %28
OpStore %2 %28
OpReturn
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<opt::LocalMultiStoreElimPass>(before, after, true,
true);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// No optimization in the presence of