Skip to content

Commit 8bf4b62

Browse files
Adding deduplication of NonWritable OpLoads
The current logic would only mark a load as read-only if either the OpVariable had NonWritable or a Uniform was not a storage buffer. This adds `IsVulkanReadOnlyStorageBuffer`, which does mostly the same stuff as `IsVulkanStorageBuffer`, with the addition of testing for NonWritable. When used as part of `IsReadOnlyLoad` for assigning value numbers, prevents it from being forced a unique value. Fixes #6412
1 parent 2c1fd89 commit 8bf4b62

3 files changed

Lines changed: 101 additions & 5 deletions

File tree

source/opt/instruction.cpp

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,48 @@ bool Instruction::IsVulkanStorageBuffer() const {
419419
return false;
420420
}
421421

422+
bool Instruction::IsVulkanReadOnlyStorageBuffer() const {
423+
if (opcode() != spv::Op::OpTypePointer) {
424+
return false;
425+
}
426+
Instruction* base_type =
427+
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
428+
429+
// Unpack the optional layer of arraying.
430+
if (base_type->opcode() == spv::Op::OpTypeArray ||
431+
base_type->opcode() == spv::Op::OpTypeRuntimeArray) {
432+
base_type = context()->get_def_use_mgr()->GetDef(
433+
base_type->GetSingleWordInOperand(0));
434+
}
435+
436+
if (base_type->opcode() != spv::Op::OpTypeStruct) {
437+
return false;
438+
}
439+
440+
spv::StorageClass storage_class =
441+
spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex));
442+
443+
if (storage_class != spv::StorageClass::Uniform &&
444+
storage_class != spv::StorageClass::StorageBuffer) {
445+
return false;
446+
}
447+
448+
analysis::DecorationManager* decoration_mgr = context()->get_decoration_mgr();
449+
uint32_t base_result_id = base_type->result_id();
450+
451+
if (storage_class == spv::StorageClass::Uniform &&
452+
!decoration_mgr->HasDecoration(base_result_id,
453+
spv::Decoration::BufferBlock)) {
454+
return false;
455+
}
456+
if (storage_class == spv::StorageClass::StorageBuffer &&
457+
!decoration_mgr->HasDecoration(base_result_id, spv::Decoration::Block)) {
458+
return false;
459+
}
460+
return decoration_mgr->HasDecoration(base_result_id,
461+
spv::Decoration::NonWritable);
462+
}
463+
422464
bool Instruction::IsVulkanStorageBufferVariable() const {
423465
if (opcode() != spv::Op::OpVariable) {
424466
return false;
@@ -491,6 +533,9 @@ bool Instruction::IsReadOnlyPointerShaders() const {
491533
if (!type_def->IsVulkanStorageBuffer()) {
492534
return true;
493535
}
536+
if (type_def->IsVulkanReadOnlyStorageBuffer()) {
537+
return true;
538+
}
494539
break;
495540
case spv::StorageClass::PushConstant:
496541
case spv::StorageClass::Input:
@@ -499,11 +544,8 @@ bool Instruction::IsReadOnlyPointerShaders() const {
499544
break;
500545
}
501546

502-
bool is_nonwritable = false;
503-
context()->get_decoration_mgr()->ForEachDecoration(
504-
result_id(), uint32_t(spv::Decoration::NonWritable),
505-
[&is_nonwritable](const Instruction&) { is_nonwritable = true; });
506-
return is_nonwritable;
547+
return context()->get_decoration_mgr()->HasDecoration(
548+
result_id(), spv::Decoration::NonWritable);
507549
}
508550

509551
bool Instruction::IsReadOnlyPointerKernel() const {

source/opt/instruction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,10 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
481481
// storage buffer.
482482
bool IsVulkanStorageBuffer() const;
483483

484+
// Returns true if the instruction defines a pointer type that points to a
485+
// read-only buffer.
486+
bool IsVulkanReadOnlyStorageBuffer() const;
487+
484488
// Returns true if the instruction defines a variable in StorageBuffer or
485489
// Uniform storage class with a pointer type that points to a storage buffer.
486490
bool IsVulkanStorageBufferVariable() const;

test/opt/instruction_test.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ TEST_F(DescriptorTypeTest, StorageImage) {
347347
EXPECT_FALSE(type->IsVulkanStorageTexelBuffer());
348348
EXPECT_FALSE(type->IsVulkanStorageBuffer());
349349
EXPECT_FALSE(type->IsVulkanUniformBuffer());
350+
EXPECT_FALSE(type->IsVulkanReadOnlyStorageBuffer());
350351

351352
Instruction* variable = context->get_def_use_mgr()->GetDef(3);
352353
EXPECT_FALSE(variable->IsReadOnlyPointer());
@@ -387,6 +388,7 @@ TEST_F(DescriptorTypeTest, SampledImage) {
387388
EXPECT_FALSE(type->IsVulkanStorageTexelBuffer());
388389
EXPECT_FALSE(type->IsVulkanStorageBuffer());
389390
EXPECT_FALSE(type->IsVulkanUniformBuffer());
391+
EXPECT_FALSE(type->IsVulkanReadOnlyStorageBuffer());
390392

391393
Instruction* variable = context->get_def_use_mgr()->GetDef(3);
392394
EXPECT_TRUE(variable->IsReadOnlyPointer());
@@ -427,6 +429,7 @@ TEST_F(DescriptorTypeTest, StorageTexelBuffer) {
427429
EXPECT_TRUE(type->IsVulkanStorageTexelBuffer());
428430
EXPECT_FALSE(type->IsVulkanStorageBuffer());
429431
EXPECT_FALSE(type->IsVulkanUniformBuffer());
432+
EXPECT_FALSE(type->IsVulkanReadOnlyStorageBuffer());
430433

431434
Instruction* variable = context->get_def_use_mgr()->GetDef(3);
432435
EXPECT_FALSE(variable->IsReadOnlyPointer());
@@ -470,6 +473,7 @@ TEST_F(DescriptorTypeTest, StorageBuffer) {
470473
EXPECT_FALSE(type->IsVulkanStorageTexelBuffer());
471474
EXPECT_TRUE(type->IsVulkanStorageBuffer());
472475
EXPECT_FALSE(type->IsVulkanUniformBuffer());
476+
EXPECT_FALSE(type->IsVulkanReadOnlyStorageBuffer());
473477

474478
Instruction* variable = context->get_def_use_mgr()->GetDef(3);
475479
EXPECT_FALSE(variable->IsReadOnlyPointer());
@@ -478,6 +482,51 @@ TEST_F(DescriptorTypeTest, StorageBuffer) {
478482
EXPECT_FALSE(object_copy->IsReadOnlyPointer());
479483
}
480484

485+
TEST_F(DescriptorTypeTest, ReadOnlyStorageBuffer) {
486+
const std::string text = R"(
487+
OpCapability Shader
488+
%1 = OpExtInstImport "GLSL.std.450"
489+
OpMemoryModel Logical GLSL450
490+
OpEntryPoint Fragment %2 "main"
491+
OpExecutionMode %2 OriginUpperLeft
492+
OpSource GLSL 430
493+
OpName %3 "myStorageImage"
494+
OpDecorate %3 DescriptorSet 0
495+
OpDecorate %3 Binding 0
496+
OpDecorate %9 BufferBlock
497+
OpDecorate %9 NonWritable
498+
%4 = OpTypeVoid
499+
%5 = OpTypeFunction %4
500+
%6 = OpTypeFloat 32
501+
%7 = OpTypeVector %6 4
502+
%8 = OpTypeRuntimeArray %7
503+
%9 = OpTypeStruct %8
504+
%10 = OpTypePointer Uniform %9
505+
%3 = OpVariable %10 Uniform
506+
%2 = OpFunction %4 None %5
507+
%11 = OpLabel
508+
%12 = OpCopyObject %8 %3
509+
OpReturn
510+
OpFunctionEnd
511+
)";
512+
513+
std::unique_ptr<IRContext> context =
514+
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
515+
Instruction* type = context->get_def_use_mgr()->GetDef(10);
516+
EXPECT_FALSE(type->IsVulkanStorageImage());
517+
EXPECT_FALSE(type->IsVulkanSampledImage());
518+
EXPECT_FALSE(type->IsVulkanStorageTexelBuffer());
519+
EXPECT_TRUE(type->IsVulkanStorageBuffer());
520+
EXPECT_FALSE(type->IsVulkanUniformBuffer());
521+
EXPECT_TRUE(type->IsVulkanReadOnlyStorageBuffer());
522+
523+
Instruction* variable = context->get_def_use_mgr()->GetDef(3);
524+
EXPECT_TRUE(variable->IsReadOnlyPointer());
525+
526+
Instruction* object_copy = context->get_def_use_mgr()->GetDef(12);
527+
EXPECT_FALSE(object_copy->IsReadOnlyPointer());
528+
}
529+
481530
TEST_F(DescriptorTypeTest, UniformBuffer) {
482531
const std::string text = R"(
483532
OpCapability Shader
@@ -513,6 +562,7 @@ TEST_F(DescriptorTypeTest, UniformBuffer) {
513562
EXPECT_FALSE(type->IsVulkanStorageTexelBuffer());
514563
EXPECT_FALSE(type->IsVulkanStorageBuffer());
515564
EXPECT_TRUE(type->IsVulkanUniformBuffer());
565+
EXPECT_FALSE(type->IsVulkanReadOnlyStorageBuffer());
516566

517567
Instruction* variable = context->get_def_use_mgr()->GetDef(3);
518568
EXPECT_TRUE(variable->IsReadOnlyPointer());

0 commit comments

Comments
 (0)