spirv-fuzz: Add support for BuiltIn decoration (#3736)
Fixes #3676.
diff --git a/source/fuzz/fuzzer_pass_add_composite_types.cpp b/source/fuzz/fuzzer_pass_add_composite_types.cpp
index 653d784..c4d8d1c 100644
--- a/source/fuzz/fuzzer_pass_add_composite_types.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_types.cpp
@@ -120,10 +120,15 @@
case SpvOpTypeFloat:
case SpvOpTypeInt:
case SpvOpTypeMatrix:
- case SpvOpTypeStruct:
case SpvOpTypeVector:
candidates.push_back(inst.result_id());
break;
+ case SpvOpTypeStruct: {
+ if (!fuzzerutil::MembersHaveBuiltInDecoration(GetIRContext(),
+ inst.result_id())) {
+ candidates.push_back(inst.result_id());
+ }
+ } break;
default:
break;
}
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 3883cbe..aa45d66 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -1270,6 +1270,15 @@
const auto* type = ir_context->get_type_mgr()->GetType(type_id);
(void)type; // Make compiler happy in release mode.
assert(type && !type->AsFunction() && "Component's type id is invalid");
+
+ if (type->AsStruct()) {
+ // From the spec for the BuiltIn decoration:
+ // - When applied to a structure-type member, that structure type cannot
+ // be contained as a member of another structure type.
+ assert(!MembersHaveBuiltInDecoration(ir_context, type_id) &&
+ "A member struct has BuiltIn members");
+ }
+
operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}});
}
@@ -1452,6 +1461,31 @@
return true;
}
+bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context,
+ uint32_t struct_type_id) {
+ const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(struct_type_id);
+ assert(type_inst && type_inst->opcode() == SpvOpTypeStruct &&
+ "|struct_type_id| is not a result id of an OpTypeStruct");
+
+ uint32_t builtin_count = 0;
+ ir_context->get_def_use_mgr()->ForEachUser(
+ type_inst,
+ [struct_type_id, &builtin_count](const opt::Instruction* user) {
+ if (user->opcode() == SpvOpMemberDecorate &&
+ user->GetSingleWordInOperand(0) == struct_type_id &&
+ static_cast<SpvDecoration>(user->GetSingleWordInOperand(2)) ==
+ SpvDecorationBuiltIn) {
+ ++builtin_count;
+ }
+ });
+
+ assert((builtin_count == 0 || builtin_count == type_inst->NumInOperands()) &&
+ "The module is invalid: either none or all of the members of "
+ "|struct_type_id| may be builtin");
+
+ return builtin_count != 0;
+}
+
} // namespace fuzzerutil
} // namespace fuzz
} // namespace spvtools
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index c9adfd7..865c1a0 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -468,7 +468,8 @@
// Creates a new OpTypeStruct instruction in the module. Updates module's id
// bound to accommodate for |result_id|. |component_type_ids| may not contain
-// a result id of an OpTypeFunction.
+// a result id of an OpTypeFunction. if |component_type_ids| contains a result
+// of an OpTypeStruct instruction, that struct may not have BuiltIn members.
void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
const std::vector<uint32_t>& component_type_ids);
@@ -516,6 +517,13 @@
opt::Instruction* use_instruction,
uint32_t use_in_operand_index);
+// Requires that |struct_type_id| is the id of a struct type, and (as per the
+// SPIR-V spec) that either all or none of the members of |struct_type_id| have
+// the BuiltIn decoration. Returns true if and only if all members have the
+// BuiltIn decoration.
+bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context,
+ uint32_t struct_type_id);
+
} // namespace fuzzerutil
} // namespace fuzz
} // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_struct.cpp b/source/fuzz/transformation_add_type_struct.cpp
index a7345a1..e8adefd 100644
--- a/source/fuzz/transformation_add_type_struct.cpp
+++ b/source/fuzz/transformation_add_type_struct.cpp
@@ -44,6 +44,14 @@
// function type; both are illegal.
return false;
}
+
+ // From the spec for the BuiltIn decoration:
+ // - When applied to a structure-type member, that structure type cannot
+ // be contained as a member of another structure type.
+ if (type->AsStruct() &&
+ fuzzerutil::MembersHaveBuiltInDecoration(ir_context, member_type)) {
+ return false;
+ }
}
return true;
}
diff --git a/source/fuzz/transformation_add_type_struct.h b/source/fuzz/transformation_add_type_struct.h
index 86a532d..c9d0cfd 100644
--- a/source/fuzz/transformation_add_type_struct.h
+++ b/source/fuzz/transformation_add_type_struct.h
@@ -35,6 +35,9 @@
// - |message_.fresh_id| must be a fresh id
// - |message_.member_type_id| must be a sequence of non-function type ids
+ // - |message_.member_type_id| may not contain a result id of an OpTypeStruct
+ // instruction with BuiltIn members (i.e. members of the struct are
+ // decorated via OpMemberDecorate with BuiltIn decoration).
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
diff --git a/test/fuzz/transformation_add_type_struct_test.cpp b/test/fuzz/transformation_add_type_struct_test.cpp
index 06f78cd..61e87a5 100644
--- a/test/fuzz/transformation_add_type_struct_test.cpp
+++ b/test/fuzz/transformation_add_type_struct_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "source/fuzz/transformation_add_type_struct.h"
+
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
@@ -109,6 +110,49 @@
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
+TEST(TransformationAddTypeStructTest, HandlesBuiltInMembers) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %2 "main"
+ OpMemberDecorate %4 0 BuiltIn Position
+ OpMemberDecorate %4 1 BuiltIn PointSize
+ OpMemberDecorate %4 2 BuiltIn ClipDistance
+ %6 = OpTypeFloat 32
+ %5 = OpTypeVector %6 4
+ %9 = OpTypeInt 32 1
+ %8 = OpConstant %9 1
+ %7 = OpTypeArray %6 %8
+ %4 = OpTypeStruct %5 %6 %7
+ %27 = OpTypeVoid
+ %28 = OpTypeFunction %27
+ %2 = OpFunction %27 None %28
+ %29 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // From the spec for the BuiltIn decoration:
+ // - When applied to a structure-type member, that structure type cannot
+ // be contained as a member of another structure type.
+ //
+ // OpTypeStruct with id %4 has BuiltIn members.
+ ASSERT_FALSE(TransformationAddTypeStruct(50, {6, 5, 4, 6, 7})
+ .IsApplicable(context.get(), transformation_context));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools