2019-06-18 20:41:08 +03:00
|
|
|
// 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/fuzz/transformation_replace_constant_with_uniform.h"
|
|
|
|
|
|
|
|
#include "source/fuzz/fuzzer_util.h"
|
2019-06-19 22:45:14 +03:00
|
|
|
#include "source/fuzz/uniform_buffer_element_descriptor.h"
|
2019-06-18 20:41:08 +03:00
|
|
|
|
|
|
|
namespace spvtools {
|
|
|
|
namespace fuzz {
|
|
|
|
|
2019-06-25 22:49:46 +03:00
|
|
|
TransformationReplaceConstantWithUniform::
|
|
|
|
TransformationReplaceConstantWithUniform(
|
2021-03-23 16:31:44 +03:00
|
|
|
protobufs::TransformationReplaceConstantWithUniform message)
|
|
|
|
: message_(std::move(message)) {}
|
2019-06-25 22:49:46 +03:00
|
|
|
|
|
|
|
TransformationReplaceConstantWithUniform::
|
|
|
|
TransformationReplaceConstantWithUniform(
|
|
|
|
protobufs::IdUseDescriptor id_use,
|
|
|
|
protobufs::UniformBufferElementDescriptor uniform_descriptor,
|
|
|
|
uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load) {
|
|
|
|
*message_.mutable_id_use_descriptor() = std::move(id_use);
|
|
|
|
*message_.mutable_uniform_descriptor() = std::move(uniform_descriptor);
|
|
|
|
message_.set_fresh_id_for_access_chain(fresh_id_for_access_chain);
|
|
|
|
message_.set_fresh_id_for_load(fresh_id_for_load);
|
|
|
|
}
|
2019-06-18 20:41:08 +03:00
|
|
|
|
2019-06-25 22:49:46 +03:00
|
|
|
std::unique_ptr<opt::Instruction>
|
|
|
|
TransformationReplaceConstantWithUniform::MakeAccessChainInstruction(
|
2020-04-02 17:54:46 +03:00
|
|
|
spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const {
|
2019-06-18 20:41:08 +03:00
|
|
|
// The input operands for the access chain.
|
|
|
|
opt::Instruction::OperandList operands_for_access_chain;
|
|
|
|
|
2019-06-19 22:45:14 +03:00
|
|
|
opt::Instruction* uniform_variable =
|
2020-04-02 17:54:46 +03:00
|
|
|
FindUniformVariable(message_.uniform_descriptor(), ir_context, false);
|
2019-06-19 22:45:14 +03:00
|
|
|
|
2019-06-18 20:41:08 +03:00
|
|
|
// The first input operand is the id of the uniform variable.
|
|
|
|
operands_for_access_chain.push_back(
|
2019-06-19 22:45:14 +03:00
|
|
|
{SPV_OPERAND_TYPE_ID, {uniform_variable->result_id()}});
|
2019-06-18 20:41:08 +03:00
|
|
|
|
|
|
|
// The other input operands are the ids of the constants used to index into
|
|
|
|
// the uniform. The uniform buffer descriptor specifies a series of literals;
|
|
|
|
// for each we find the id of the instruction that defines it, and add these
|
|
|
|
// instruction ids as operands.
|
|
|
|
opt::analysis::Integer int_type(32, true);
|
|
|
|
auto registered_int_type =
|
2020-04-02 17:54:46 +03:00
|
|
|
ir_context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
|
|
|
|
auto int_type_id = ir_context->get_type_mgr()->GetId(&int_type);
|
2019-06-25 22:49:46 +03:00
|
|
|
for (auto index : message_.uniform_descriptor().index()) {
|
2019-06-18 20:41:08 +03:00
|
|
|
opt::analysis::IntConstant int_constant(registered_int_type, {index});
|
2020-04-02 17:54:46 +03:00
|
|
|
auto constant_id = ir_context->get_constant_mgr()->FindDeclaredConstant(
|
2019-06-18 20:41:08 +03:00
|
|
|
&int_constant, int_type_id);
|
|
|
|
operands_for_access_chain.push_back({SPV_OPERAND_TYPE_ID, {constant_id}});
|
|
|
|
}
|
|
|
|
|
|
|
|
// The type id for the access chain is a uniform pointer with base type
|
|
|
|
// matching the given constant id type.
|
2020-04-02 17:54:46 +03:00
|
|
|
auto type_and_pointer_type =
|
|
|
|
ir_context->get_type_mgr()->GetTypeAndPointerType(constant_type_id,
|
|
|
|
SpvStorageClassUniform);
|
2019-06-18 20:41:08 +03:00
|
|
|
assert(type_and_pointer_type.first != nullptr);
|
|
|
|
assert(type_and_pointer_type.second != nullptr);
|
|
|
|
auto pointer_to_uniform_constant_type_id =
|
2020-04-02 17:54:46 +03:00
|
|
|
ir_context->get_type_mgr()->GetId(type_and_pointer_type.second.get());
|
2019-06-18 20:41:08 +03:00
|
|
|
|
|
|
|
return MakeUnique<opt::Instruction>(
|
2020-04-02 17:54:46 +03:00
|
|
|
ir_context, SpvOpAccessChain, pointer_to_uniform_constant_type_id,
|
2019-06-25 22:49:46 +03:00
|
|
|
message_.fresh_id_for_access_chain(), operands_for_access_chain);
|
2019-06-18 20:41:08 +03:00
|
|
|
}
|
|
|
|
|
2019-06-25 22:49:46 +03:00
|
|
|
std::unique_ptr<opt::Instruction>
|
|
|
|
TransformationReplaceConstantWithUniform::MakeLoadInstruction(
|
2020-04-02 17:54:46 +03:00
|
|
|
spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const {
|
2019-06-18 20:41:08 +03:00
|
|
|
opt::Instruction::OperandList operands_for_load = {
|
2019-06-25 22:49:46 +03:00
|
|
|
{SPV_OPERAND_TYPE_ID, {message_.fresh_id_for_access_chain()}}};
|
2020-04-02 17:54:46 +03:00
|
|
|
return MakeUnique<opt::Instruction>(ir_context, SpvOpLoad, constant_type_id,
|
2019-06-25 22:49:46 +03:00
|
|
|
message_.fresh_id_for_load(),
|
2019-06-18 20:41:08 +03:00
|
|
|
operands_for_load);
|
|
|
|
}
|
|
|
|
|
2020-08-05 21:17:27 +03:00
|
|
|
opt::Instruction*
|
|
|
|
TransformationReplaceConstantWithUniform::GetInsertBeforeInstruction(
|
|
|
|
opt::IRContext* ir_context) const {
|
|
|
|
auto* result =
|
|
|
|
FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
|
|
|
|
if (!result) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The use might be in an OpPhi instruction.
|
|
|
|
if (result->opcode() == SpvOpPhi) {
|
|
|
|
// OpPhi instructions must be the first instructions in a block. Thus, we
|
|
|
|
// can't insert above the OpPhi instruction. Given the predecessor block
|
|
|
|
// that corresponds to the id use, get the last instruction in that block
|
|
|
|
// above which we can insert OpAccessChain and OpLoad.
|
|
|
|
return fuzzerutil::GetLastInsertBeforeInstruction(
|
|
|
|
ir_context,
|
|
|
|
result->GetSingleWordInOperand(
|
|
|
|
message_.id_use_descriptor().in_operand_index() + 1),
|
|
|
|
SpvOpLoad);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The only operand that we could've replaced in the OpBranchConditional is
|
|
|
|
// the condition id. But that operand has a boolean type and uniform variables
|
|
|
|
// can't store booleans (see the spec on OpTypeBool). Thus, |result| can't be
|
|
|
|
// an OpBranchConditional.
|
|
|
|
assert(result->opcode() != SpvOpBranchConditional &&
|
|
|
|
"OpBranchConditional has no operands to replace");
|
|
|
|
|
|
|
|
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, result) &&
|
|
|
|
"We should be able to insert OpLoad and OpAccessChain at this point");
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-06-25 22:49:46 +03:00
|
|
|
bool TransformationReplaceConstantWithUniform::IsApplicable(
|
2020-04-02 17:54:46 +03:00
|
|
|
opt::IRContext* ir_context,
|
|
|
|
const TransformationContext& transformation_context) const {
|
2019-06-18 20:41:08 +03:00
|
|
|
// The following is really an invariant of the transformation rather than
|
|
|
|
// merely a requirement of the precondition. We check it here since we cannot
|
2019-06-25 22:49:46 +03:00
|
|
|
// check it in the message_ constructor.
|
|
|
|
assert(message_.fresh_id_for_access_chain() != message_.fresh_id_for_load() &&
|
2019-06-18 20:41:08 +03:00
|
|
|
"Fresh ids for access chain and load result cannot be the same.");
|
|
|
|
|
|
|
|
// The ids for the access chain and load instructions must both be fresh.
|
2020-04-02 17:54:46 +03:00
|
|
|
if (!fuzzerutil::IsFreshId(ir_context,
|
|
|
|
message_.fresh_id_for_access_chain())) {
|
2019-06-18 20:41:08 +03:00
|
|
|
return false;
|
|
|
|
}
|
2020-04-02 17:54:46 +03:00
|
|
|
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id_for_load())) {
|
2019-06-18 20:41:08 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The id specified in the id use descriptor must be that of a declared scalar
|
|
|
|
// constant.
|
2020-04-02 17:54:46 +03:00
|
|
|
auto declared_constant = ir_context->get_constant_mgr()->FindDeclaredConstant(
|
2019-06-25 22:49:46 +03:00
|
|
|
message_.id_use_descriptor().id_of_interest());
|
2019-06-18 20:41:08 +03:00
|
|
|
if (!declared_constant) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!declared_constant->AsScalarConstant()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The fact manager needs to believe that the uniform data element described
|
|
|
|
// by the uniform buffer element descriptor will hold a scalar value.
|
|
|
|
auto constant_id_associated_with_uniform =
|
2020-04-02 17:54:46 +03:00
|
|
|
transformation_context.GetFactManager()->GetConstantFromUniformDescriptor(
|
2020-09-24 14:27:59 +03:00
|
|
|
message_.uniform_descriptor());
|
2019-06-18 20:41:08 +03:00
|
|
|
if (!constant_id_associated_with_uniform) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto constant_associated_with_uniform =
|
2020-04-02 17:54:46 +03:00
|
|
|
ir_context->get_constant_mgr()->FindDeclaredConstant(
|
2019-06-18 20:41:08 +03:00
|
|
|
constant_id_associated_with_uniform);
|
|
|
|
assert(constant_associated_with_uniform &&
|
|
|
|
"The constant should be present in the module.");
|
|
|
|
if (!constant_associated_with_uniform->AsScalarConstant()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The types and values of the scalar value held in the id specified by the id
|
|
|
|
// use descriptor and in the uniform data element specified by the uniform
|
|
|
|
// buffer element descriptor need to match on both type and value.
|
|
|
|
if (!declared_constant->type()->IsSame(
|
|
|
|
constant_associated_with_uniform->type())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (declared_constant->AsScalarConstant()->words() !=
|
|
|
|
constant_associated_with_uniform->AsScalarConstant()->words()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The id use descriptor must identify some instruction with respect to the
|
|
|
|
// module.
|
|
|
|
auto instruction_using_constant =
|
2020-04-02 17:54:46 +03:00
|
|
|
FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
|
2019-06-18 20:41:08 +03:00
|
|
|
if (!instruction_using_constant) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-02-11 02:22:34 +03:00
|
|
|
// The use must not be a variable initializer; these are required to be
|
|
|
|
// constants, so it would be illegal to replace one with a uniform access.
|
|
|
|
if (instruction_using_constant->opcode() == SpvOpVariable) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-06-18 20:41:08 +03:00
|
|
|
// The module needs to have a uniform pointer type suitable for indexing into
|
|
|
|
// the uniform variable, i.e. matching the type of the constant we wish to
|
|
|
|
// replace with a uniform.
|
|
|
|
opt::analysis::Pointer pointer_to_type_of_constant(declared_constant->type(),
|
|
|
|
SpvStorageClassUniform);
|
2020-04-02 17:54:46 +03:00
|
|
|
if (!ir_context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) {
|
2019-06-18 20:41:08 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In order to index into the uniform, the module has got to contain the int32
|
|
|
|
// type, plus an OpConstant for each of the indices of interest.
|
|
|
|
opt::analysis::Integer int_type(32, true);
|
2020-04-02 17:54:46 +03:00
|
|
|
if (!ir_context->get_type_mgr()->GetId(&int_type)) {
|
2019-06-18 20:41:08 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto registered_int_type =
|
2020-04-02 17:54:46 +03:00
|
|
|
ir_context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
|
|
|
|
auto int_type_id = ir_context->get_type_mgr()->GetId(&int_type);
|
2019-06-25 22:49:46 +03:00
|
|
|
for (auto index : message_.uniform_descriptor().index()) {
|
2019-06-18 20:41:08 +03:00
|
|
|
opt::analysis::IntConstant int_constant(registered_int_type, {index});
|
2020-04-02 17:54:46 +03:00
|
|
|
if (!ir_context->get_constant_mgr()->FindDeclaredConstant(&int_constant,
|
|
|
|
int_type_id)) {
|
2019-06-18 20:41:08 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-05 21:17:27 +03:00
|
|
|
// Once all checks are completed, we should be able to safely insert
|
|
|
|
// OpAccessChain and OpLoad into the module.
|
|
|
|
assert(GetInsertBeforeInstruction(ir_context) &&
|
|
|
|
"There must exist an instruction that we can use to insert "
|
|
|
|
"OpAccessChain and OpLoad above");
|
|
|
|
|
2019-06-18 20:41:08 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-06-25 22:49:46 +03:00
|
|
|
void TransformationReplaceConstantWithUniform::Apply(
|
2020-04-02 17:54:46 +03:00
|
|
|
spvtools::opt::IRContext* ir_context,
|
|
|
|
TransformationContext* /*unused*/) const {
|
2019-06-18 20:41:08 +03:00
|
|
|
// Get the instruction that contains the id use we wish to replace.
|
2020-08-05 21:17:27 +03:00
|
|
|
auto* instruction_containing_constant_use =
|
2020-04-02 17:54:46 +03:00
|
|
|
FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
|
2019-06-18 20:41:08 +03:00
|
|
|
assert(instruction_containing_constant_use &&
|
|
|
|
"Precondition requires that the id use can be found.");
|
|
|
|
assert(instruction_containing_constant_use->GetSingleWordInOperand(
|
2019-06-25 22:49:46 +03:00
|
|
|
message_.id_use_descriptor().in_operand_index()) ==
|
|
|
|
message_.id_use_descriptor().id_of_interest() &&
|
2019-06-18 20:41:08 +03:00
|
|
|
"Does not appear to be a usage of the desired id.");
|
|
|
|
|
|
|
|
// The id of the type for the constant whose use we wish to replace.
|
|
|
|
auto constant_type_id =
|
2020-04-02 17:54:46 +03:00
|
|
|
ir_context->get_def_use_mgr()
|
2019-06-25 22:49:46 +03:00
|
|
|
->GetDef(message_.id_use_descriptor().id_of_interest())
|
2019-06-18 20:41:08 +03:00
|
|
|
->type_id();
|
|
|
|
|
2020-08-05 21:17:27 +03:00
|
|
|
// Get an instruction that will be used to insert OpAccessChain and OpLoad.
|
|
|
|
auto* insert_before_inst = GetInsertBeforeInstruction(ir_context);
|
|
|
|
assert(insert_before_inst &&
|
|
|
|
"There must exist an insertion point for OpAccessChain and OpLoad");
|
|
|
|
|
2019-06-18 20:41:08 +03:00
|
|
|
// Add an access chain instruction to target the uniform element.
|
2020-08-05 21:17:27 +03:00
|
|
|
insert_before_inst->InsertBefore(
|
2020-04-02 17:54:46 +03:00
|
|
|
MakeAccessChainInstruction(ir_context, constant_type_id));
|
2019-06-18 20:41:08 +03:00
|
|
|
|
|
|
|
// Add a load from this access chain.
|
2020-08-05 21:17:27 +03:00
|
|
|
insert_before_inst->InsertBefore(
|
2020-04-02 17:54:46 +03:00
|
|
|
MakeLoadInstruction(ir_context, constant_type_id));
|
2019-06-18 20:41:08 +03:00
|
|
|
|
|
|
|
// Adjust the instruction containing the usage of the constant so that this
|
|
|
|
// usage refers instead to the result of the load.
|
|
|
|
instruction_containing_constant_use->SetInOperand(
|
2019-06-25 22:49:46 +03:00
|
|
|
message_.id_use_descriptor().in_operand_index(),
|
|
|
|
{message_.fresh_id_for_load()});
|
2019-06-18 20:41:08 +03:00
|
|
|
|
|
|
|
// Update the module id bound to reflect the new instructions.
|
2020-04-02 17:54:46 +03:00
|
|
|
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_load());
|
|
|
|
fuzzerutil::UpdateModuleIdBound(ir_context,
|
2019-06-25 22:49:46 +03:00
|
|
|
message_.fresh_id_for_access_chain());
|
2019-06-18 20:41:08 +03:00
|
|
|
|
2020-04-02 17:54:46 +03:00
|
|
|
ir_context->InvalidateAnalysesExceptFor(
|
|
|
|
opt::IRContext::Analysis::kAnalysisNone);
|
2019-06-18 20:41:08 +03:00
|
|
|
}
|
|
|
|
|
2019-06-25 22:49:46 +03:00
|
|
|
protobufs::Transformation TransformationReplaceConstantWithUniform::ToMessage()
|
|
|
|
const {
|
|
|
|
protobufs::Transformation result;
|
|
|
|
*result.mutable_replace_constant_with_uniform() = message_;
|
2019-06-18 20:41:08 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-09-30 00:12:49 +03:00
|
|
|
std::unordered_set<uint32_t>
|
|
|
|
TransformationReplaceConstantWithUniform::GetFreshIds() const {
|
|
|
|
return {message_.fresh_id_for_access_chain(), message_.fresh_id_for_load()};
|
|
|
|
}
|
|
|
|
|
2019-06-18 20:41:08 +03:00
|
|
|
} // namespace fuzz
|
|
|
|
} // namespace spvtools
|