Fixed --eliminate-common-uniform so that it does not eliminate loads of volatile variables.

This commit is contained in:
Daniel Schürmann 2017-10-24 13:24:08 +02:00 committed by David Neto
parent 98072b749f
commit 97990dc907
3 changed files with 463 additions and 3 deletions

View File

@ -15,9 +15,7 @@
// limitations under the License.
#include "common_uniform_elim_pass.h"
#include "cfa.h"
#include "iterator.h"
namespace spvtools {
namespace opt {
@ -130,6 +128,68 @@ ir::Instruction* CommonUniformElimPass::GetPtr(
return ptrInst;
}
bool CommonUniformElimPass::IsVolatileStruct(uint32_t type_id) {
assert(def_use_mgr_->GetDef(type_id)->opcode() == SpvOpTypeStruct);
bool has_volatile_deco = false;
dec_mgr_->ForEachDecoration(type_id, SpvDecorationVolatile,
[&has_volatile_deco](const ir::Instruction&){ has_volatile_deco = true;});
return has_volatile_deco;
}
bool CommonUniformElimPass::IsAccessChainToVolatileStructType(const ir::Instruction &AccessChainInst) {
assert(AccessChainInst.opcode() == SpvOpAccessChain);
uint32_t ptr_id = AccessChainInst.GetSingleWordInOperand(0);
const ir::Instruction* ptr_inst = def_use_mgr_->GetDef(ptr_id);
uint32_t pointee_type_id = GetPointeeTypeId(ptr_inst);
const uint32_t num_operands = AccessChainInst.NumOperands();
// walk the type tree:
for (uint32_t idx = 3; idx < num_operands; ++idx) {
ir::Instruction* pointee_type = def_use_mgr_->GetDef(pointee_type_id);
switch (pointee_type->opcode()) {
case SpvOpTypeMatrix:
case SpvOpTypeVector:
case SpvOpTypeArray:
case SpvOpTypeRuntimeArray:
pointee_type_id = pointee_type->GetSingleWordOperand(1);
break;
case SpvOpTypeStruct:
// check for volatile decorations:
if (IsVolatileStruct(pointee_type_id)) return true;
if (idx < num_operands - 1) {
const uint32_t index_id = AccessChainInst.GetSingleWordOperand(idx);
const ir::Instruction* index_inst = def_use_mgr_->GetDef(index_id);
uint32_t index_value = index_inst->GetSingleWordOperand(2); // TODO: replace with GetUintValueFromConstant()
pointee_type_id = pointee_type->GetSingleWordInOperand(index_value);
}
break;
default:
assert(false && "Unhandled pointee type.");
}
}
return false;
}
bool CommonUniformElimPass::IsVolatileLoad(const ir::Instruction& loadInst) {
assert(loadInst.opcode() == SpvOpLoad);
// Check if this Load instruction has Volatile Memory Access flag
if (loadInst.NumOperands() == 4) {
uint32_t memory_access_mask = loadInst.GetSingleWordOperand(3);
if (memory_access_mask & SpvMemoryAccessVolatileMask)
return true;
}
// If we load a struct directly (result type is struct),
// check if the struct is decorated volatile
uint32_t type_id = loadInst.type_id();
if (def_use_mgr_->GetDef(type_id)->opcode() == SpvOpTypeStruct)
return IsVolatileStruct(type_id);
else
return false;
}
bool CommonUniformElimPass::IsUniformVar(uint32_t varId) {
const ir::Instruction* varInst =
def_use_mgr_->id_to_defs().find(varId)->second;
@ -301,6 +361,10 @@ bool CommonUniformElimPass::UniformAccessChainConvert(ir::Function* func) {
continue;
if (HasUnsupportedDecorates(ptrInst->result_id()))
continue;
if (IsVolatileLoad(*ii))
continue;
if (IsAccessChainToVolatileStructType(*ptrInst))
continue;
std::vector<std::unique_ptr<ir::Instruction>> newInsts;
uint32_t replId;
GenACLoadRepl(ptrInst, &newInsts, &replId);
@ -386,6 +450,8 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) {
continue;
if (HasUnsupportedDecorates(ii->result_id()))
continue;
if (IsVolatileLoad(*ii))
continue;
uint32_t replId;
const auto uItr = uniform2load_id_.find(varId);
if (uItr != uniform2load_id_.end()) {
@ -438,6 +504,8 @@ bool CommonUniformElimPass::CommonUniformLoadElimBlock(ir::Function* func) {
continue;
if (HasUnsupportedDecorates(ii->result_id()))
continue;
if (IsVolatileLoad(*ii))
continue;
uint32_t replId;
const auto uItr = uniform2load_id_.find(varId);
if (uItr != uniform2load_id_.end()) {
@ -527,6 +595,7 @@ void CommonUniformElimPass::Initialize(ir::Module* module) {
// TODO(greg-lunarg): Use def/use from previous pass
def_use_mgr_.reset(new analysis::DefUseManager(consumer(), module_));
dec_mgr_.reset(new analysis::DecorationManager(module_));
// Initialize next unused Id.
next_id_ = module->id_bound();

View File

@ -25,6 +25,7 @@
#include <utility>
#include "def_use_manager.h"
#include "decoration_manager.h"
#include "module.h"
#include "basic_block.h"
#include "pass.h"
@ -66,6 +67,20 @@ class CommonUniformElimPass : public Pass {
// Return true if variable is uniform
bool IsUniformVar(uint32_t varId);
// Given the type id for a struct type, checks if the struct type
// or any struct member is volatile decorated
bool IsVolatileStruct(uint32_t type_id);
// Given an OpAccessChain instruction, return true
// if the accessed variable belongs to a volatile
// decorated object or member of a struct type
bool IsAccessChainToVolatileStructType(const ir::Instruction &AccessChainInst);
// Given an OpLoad instruction, return true if
// OpLoad has a Volatile Memory Access flag or if
// the resulting type is a volatile decorated struct
bool IsVolatileLoad(const ir::Instruction& loadInst);
// Return true if any uses of |id| are decorate ops.
bool HasUnsupportedDecorates(uint32_t id) const;
@ -179,6 +194,9 @@ class CommonUniformElimPass : public Pass {
// Def-Uses for the module we are processing
std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
// Decorations for the module we are processing
std::unique_ptr<analysis::DecorationManager> dec_mgr_;
// Map from block's label id to block.
std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;

View File

@ -14,7 +14,6 @@
// limitations under the License.
#include "pass_fixture.h"
#include "pass_utils.h"
namespace {
@ -664,6 +663,380 @@ OpFunctionEnd
predefs + before, predefs + after, true, true);
}
TEST_F(CommonUniformElimTest, Volatile1) {
// Note: This test exemplifies the following:
// - Same test as Basic1 with the exception that
// the Load of g_F in else-branch is volatile
// - Common uniform (%_) load floated to nearest non-controlled block
//
// #version 140
// in vec4 BaseColor;
// in float fi;
//
// layout(std140) uniform U_t
// {
// float g_F;
// float g_F2;
// } ;
//
// void main()
// {
// vec4 v = BaseColor;
// if (fi > 0) {
// v = v * g_F;
// }
// else {
// float f2 = g_F2 - g_F;
// v = v * f2;
// }
// gl_FragColor = v;
// }
const std::string predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %fi "fi"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_F"
OpMemberName %U_t 1 "g_F2"
OpName %_ ""
OpName %f2 "f2"
OpName %gl_FragColor "gl_FragColor"
OpMemberDecorate %U_t 0 Offset 0
OpMemberDecorate %U_t 1 Offset 4
OpDecorate %U_t Block
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%fi = OpVariable %_ptr_Input_float Input
%float_0 = OpConstant %float 0
%bool = OpTypeBool
%U_t = OpTypeStruct %float %float
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%_ptr_Function_float = OpTypePointer Function %float
%int_1 = OpConstant %int 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";
const std::string before =
R"(%main = OpFunction %void None %11
%26 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%f2 = OpVariable %_ptr_Function_float Function
%27 = OpLoad %v4float %BaseColor
OpStore %v %27
%28 = OpLoad %float %fi
%29 = OpFOrdGreaterThan %bool %28 %float_0
OpSelectionMerge %30 None
OpBranchConditional %29 %31 %32
%31 = OpLabel
%33 = OpLoad %v4float %v
%34 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%35 = OpLoad %float %34
%36 = OpVectorTimesScalar %v4float %33 %35
OpStore %v %36
OpBranch %30
%32 = OpLabel
%37 = OpAccessChain %_ptr_Uniform_float %_ %int_1
%38 = OpLoad %float %37
%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%40 = OpLoad %float %39 Volatile
%41 = OpFSub %float %38 %40
OpStore %f2 %41
%42 = OpLoad %v4float %v
%43 = OpLoad %float %f2
%44 = OpVectorTimesScalar %v4float %42 %43
OpStore %v %44
OpBranch %30
%30 = OpLabel
%45 = OpLoad %v4float %v
OpStore %gl_FragColor %45
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %11
%26 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%f2 = OpVariable %_ptr_Function_float Function
%50 = OpLoad %U_t %_
%27 = OpLoad %v4float %BaseColor
OpStore %v %27
%28 = OpLoad %float %fi
%29 = OpFOrdGreaterThan %bool %28 %float_0
OpSelectionMerge %30 None
OpBranchConditional %29 %31 %32
%31 = OpLabel
%33 = OpLoad %v4float %v
%47 = OpCompositeExtract %float %50 0
%36 = OpVectorTimesScalar %v4float %33 %47
OpStore %v %36
OpBranch %30
%32 = OpLabel
%49 = OpCompositeExtract %float %50 1
%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%40 = OpLoad %float %39 Volatile
%41 = OpFSub %float %49 %40
OpStore %f2 %41
%42 = OpLoad %v4float %v
%43 = OpLoad %float %f2
%44 = OpVectorTimesScalar %v4float %42 %43
OpStore %v %44
OpBranch %30
%30 = OpLabel
%45 = OpLoad %v4float %v
OpStore %gl_FragColor %45
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::CommonUniformElimPass>(
predefs + before, predefs + after, true, true);
}
TEST_F(CommonUniformElimTest, Volatile2) {
// Note: This test exemplifies the following:
// - Same test as Basic1 with the exception that
// U_t is Volatile.
// - No optimizations are applied
//
// #version 430
// in vec4 BaseColor;
// in float fi;
//
// layout(std430) volatile buffer U_t
// {
// float g_F;
// float g_F2;
// };
//
//
// void main(void)
// {
// vec4 v = BaseColor;
// if (fi > 0) {
// v = v * g_F;
// } else {
// float f2 = g_F2 - g_F;
// v = v * f2;
// }
// }
const std::string text =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %fi
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 430
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %fi "fi"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_F"
OpMemberName %U_t 1 "g_F2"
OpName %_ ""
OpName %f2 "f2"
OpDecorate %BaseColor Location 0
OpDecorate %fi Location 0
OpMemberDecorate %U_t 0 Volatile
OpMemberDecorate %U_t 0 Offset 0
OpMemberDecorate %U_t 1 Volatile
OpMemberDecorate %U_t 1 Offset 4
OpDecorate %U_t BufferBlock
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%fi = OpVariable %_ptr_Input_float Input
%float_0 = OpConstant %float 0
%bool = OpTypeBool
%U_t = OpTypeStruct %float %float
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%_ptr_Function_float = OpTypePointer Function %float
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%f2 = OpVariable %_ptr_Function_float Function
%12 = OpLoad %v4float %BaseColor
OpStore %v %12
%15 = OpLoad %float %fi
%18 = OpFOrdGreaterThan %bool %15 %float_0
OpSelectionMerge %20 None
OpBranchConditional %18 %19 %31
%19 = OpLabel
%21 = OpLoad %v4float %v
%28 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%29 = OpLoad %float %28
%30 = OpVectorTimesScalar %v4float %21 %29
OpStore %v %30
OpBranch %20
%31 = OpLabel
%35 = OpAccessChain %_ptr_Uniform_float %_ %int_1
%36 = OpLoad %float %35
%37 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%38 = OpLoad %float %37
%39 = OpFSub %float %36 %38
OpStore %f2 %39
%40 = OpLoad %v4float %v
%41 = OpLoad %float %f2
%42 = OpVectorTimesScalar %v4float %40 %41
OpStore %v %42
OpBranch %20
%20 = OpLabel
OpReturn
OpFunctionEnd
)";
opt::Pass::Status res = std::get<1>(SinglePassRunAndDisassemble<opt::CommonUniformElimPass>(text, true));
EXPECT_EQ(res, opt::Pass::Status::SuccessWithoutChange);
}
TEST_F(CommonUniformElimTest, Volatile3) {
// Note: This test exemplifies the following:
// - Same test as Volatile2 with the exception that
// the nested struct S is volatile
// - No optimizations are applied
//
// #version 430
// in vec4 BaseColor;
// in float fi;
//
// struct S {
// volatile float a;
// };
//
// layout(std430) buffer U_t
// {
// S g_F;
// S g_F2;
// };
//
//
// void main(void)
// {
// vec4 v = BaseColor;
// if (fi > 0) {
// v = v * g_F.a;
// } else {
// float f2 = g_F2.a - g_F.a;
// v = v * f2;
// }
// }
const std::string text =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %fi
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 430
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %fi "fi"
OpName %S "S"
OpMemberName %S 0 "a"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_F"
OpMemberName %U_t 1 "g_F2"
OpName %_ ""
OpName %f2 "f2"
OpDecorate %BaseColor Location 0
OpDecorate %fi Location 0
OpMemberDecorate %S 0 Offset 0
OpMemberDecorate %S 0 Volatile
OpMemberDecorate %U_t 0 Offset 0
OpMemberDecorate %U_t 1 Offset 4
OpDecorate %U_t BufferBlock
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%fi = OpVariable %_ptr_Input_float Input
%float_0 = OpConstant %float 0
%bool = OpTypeBool
%S = OpTypeStruct %float
%U_t = OpTypeStruct %S %S
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%_ptr_Function_float = OpTypePointer Function %float
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%f2 = OpVariable %_ptr_Function_float Function
%12 = OpLoad %v4float %BaseColor
OpStore %v %12
%15 = OpLoad %float %fi
%18 = OpFOrdGreaterThan %bool %15 %float_0
OpSelectionMerge %20 None
OpBranchConditional %18 %19 %32
%19 = OpLabel
%21 = OpLoad %v4float %v
%29 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_0
%30 = OpLoad %float %29
%31 = OpVectorTimesScalar %v4float %21 %30
OpStore %v %31
OpBranch %20
%32 = OpLabel
%36 = OpAccessChain %_ptr_Uniform_float %_ %int_1 %int_0
%37 = OpLoad %float %36
%38 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_0
%39 = OpLoad %float %38
%40 = OpFSub %float %37 %39
OpStore %f2 %40
%41 = OpLoad %v4float %v
%42 = OpLoad %float %f2
%43 = OpVectorTimesScalar %v4float %41 %42
OpStore %v %43
OpBranch %20
%20 = OpLabel
OpReturn
OpFunctionEnd
)";
opt::Pass::Status res = std::get<1>(SinglePassRunAndDisassemble<opt::CommonUniformElimPass>(text, true));
EXPECT_EQ(res, opt::Pass::Status::SuccessWithoutChange);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Disqualifying cases: extensions, decorations, non-logical addressing,