Updated spirv-tools.

This commit is contained in:
Бранимир Караџић
2020-06-12 22:06:54 -07:00
parent 2cdcab3215
commit add2440710
16 changed files with 290 additions and 46 deletions

View File

@@ -1 +1 @@
"v2020.4-dev", "SPIRV-Tools v2020.4-dev 3e4a8382b6d054cbd8f0a04a4f83c4350dd803ea"
"v2020.4-dev", "SPIRV-Tools v2020.4-dev 2cdcab3215d654e18c3b6d5f12bd94e7e64fb8b1"

View File

@@ -90,6 +90,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_global_variable.h
transformation_add_local_variable.h
transformation_add_no_contraction_decoration.h
transformation_add_spec_constant_op.h
transformation_add_type_array.h
transformation_add_type_boolean.h
transformation_add_type_float.h
@@ -187,6 +188,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_global_variable.cpp
transformation_add_local_variable.cpp
transformation_add_no_contraction_decoration.cpp
transformation_add_spec_constant_op.cpp
transformation_add_type_array.cpp
transformation_add_type_boolean.cpp
transformation_add_type_float.cpp

View File

@@ -813,8 +813,10 @@ void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts(
struct DataDescriptorPairEquals {
bool operator()(const DataDescriptorPair& first,
const DataDescriptorPair& second) const {
return DataDescriptorEquals()(&first.first, &second.first) &&
DataDescriptorEquals()(&first.second, &second.second);
return (DataDescriptorEquals()(&first.first, &second.first) &&
DataDescriptorEquals()(&first.second, &second.second)) ||
(DataDescriptorEquals()(&first.first, &second.second) &&
DataDescriptorEquals()(&first.second, &second.first));
}
};

View File

@@ -75,7 +75,7 @@ void FuzzerPassAdjustMemoryOperandsMasks::Apply() {
*inst_it, mask_index);
auto existing_mask =
existing_mask_in_operand_index < inst_it->NumInOperands()
? inst_it->GetSingleWordOperand(
? inst_it->GetSingleWordInOperand(
existing_mask_in_operand_index)
: static_cast<uint32_t>(SpvMemoryAccessMaskNone);

View File

@@ -27,6 +27,7 @@
#include "source/fuzz/transformation_add_function.h"
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_global_variable.h"
#include "source/fuzz/transformation_add_spec_constant_op.h"
#include "source/fuzz/transformation_add_type_array.h"
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
@@ -413,14 +414,41 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
argument_type_ids));
}
} break;
case SpvOpSpecConstantOp: {
new_result_id = GetFuzzerContext()->GetFreshId();
auto type_id = original_id_to_donated_id->at(type_or_value.type_id());
auto opcode = static_cast<SpvOp>(type_or_value.GetSingleWordInOperand(0));
// Make sure we take into account |original_id_to_donated_id| when
// computing operands for OpSpecConstantOp.
opt::Instruction::OperandList operands;
for (uint32_t i = 1; i < type_or_value.NumInOperands(); ++i) {
const auto& operand = type_or_value.GetInOperand(i);
auto data =
operand.type == SPV_OPERAND_TYPE_ID
? opt::Operand::OperandData{original_id_to_donated_id->at(
operand.words[0])}
: operand.words;
operands.push_back({operand.type, std::move(data)});
}
ApplyTransformation(TransformationAddSpecConstantOp(
new_result_id, type_id, opcode, std::move(operands)));
} break;
case SpvOpSpecConstantTrue:
case SpvOpSpecConstantFalse:
case SpvOpConstantTrue:
case SpvOpConstantFalse: {
// It is OK to have duplicate definitions of True and False, so add
// these to the module, using a remapped Bool type.
new_result_id = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationAddConstantBoolean(
new_result_id, type_or_value.opcode() == SpvOpConstantTrue));
auto value = type_or_value.opcode() == SpvOpConstantTrue ||
type_or_value.opcode() == SpvOpSpecConstantTrue;
ApplyTransformation(
TransformationAddConstantBoolean(new_result_id, value));
} break;
case SpvOpSpecConstant:
case SpvOpConstant: {
// It is OK to have duplicate constant definitions, so add this to the
// module using a remapped result type.
@@ -433,6 +461,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
data_words));
} break;
case SpvOpSpecConstantComposite:
case SpvOpConstantComposite: {
assert(original_id_to_donated_id->count(type_or_value.type_id()) &&
"Composite types for which it is possible to create a constant "

View File

@@ -80,12 +80,21 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
std::vector<opt::Instruction*> value_instructions =
FindAvailableInstructions(
function, block, instruction_iterator,
[basic_type_id](opt::IRContext* /*unused*/,
opt::Instruction* instruction) -> bool {
[basic_type_id, instruction_descriptor](
opt::IRContext* ir_context,
opt::Instruction* instruction) -> bool {
if (!instruction->result_id() || !instruction->type_id()) {
return false;
}
return instruction->type_id() == basic_type_id;
if (instruction->type_id() != basic_type_id) {
return false;
}
return fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context,
FindInstruction(instruction_descriptor, ir_context),
instruction->result_id());
});
if (value_instructions.empty()) {

View File

@@ -550,6 +550,39 @@ bool IsNullConstantSupported(const opt::analysis::Type& type) {
type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue();
}
bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
const opt::IRContext* ir_context) {
// TODO(afd): We capture the universal environments for which this requirement
// holds. The check should be refined on demand for other target
// environments.
switch (ir_context->grammar().target_env()) {
case SPV_ENV_UNIVERSAL_1_0:
case SPV_ENV_UNIVERSAL_1_1:
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_UNIVERSAL_1_3:
return false;
default:
return true;
}
}
void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id) {
if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(context)) {
// Conservatively add this global to the interface of every entry point in
// the module. This means that the global is available for other
// transformations to use.
//
// A downside of this is that the global will be in the interface even if it
// ends up never being used.
//
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
// this if a more thorough approach to entry point interfaces is taken.
for (auto& entry_point : context->module()->entry_points()) {
entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {id}});
}
}
}
} // namespace fuzzerutil
} // namespace fuzz

View File

@@ -215,6 +215,17 @@ uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
// to have an OpConstantNull value.
bool IsNullConstantSupported(const opt::analysis::Type& type);
// Returns true if and only if the SPIR-V version being used requires that
// global variables accessed in the static call graph of an entry point need
// to be listed in that entry point's interface.
bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
const opt::IRContext* context);
// Adds |id| into the interface of every entry point of the shader.
// Does nothing if SPIR-V doesn't require global variables, that are accessed
// from an entry point function, to be listed in that function's interface.
void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id);
} // namespace fuzzerutil
} // namespace fuzz

View File

@@ -376,6 +376,7 @@ message Transformation {
TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 45;
TransformationAdjustBranchWeights adjust_branch_weights = 46;
TransformationPushIdThroughVariable push_id_through_variable = 47;
TransformationAddSpecConstantOp add_spec_constant_op = 48;
// Add additional option using the next available number.
}
}
@@ -619,6 +620,24 @@ message TransformationAddNoContractionDecoration {
}
message TransformationAddSpecConstantOp {
// Adds OpSpecConstantOp into the module.
// Result id for the new instruction.
uint32 fresh_id = 1;
// Type id for the new instruction.
uint32 type_id = 2;
// Opcode operand of the OpSpecConstantOp instruction.
uint32 opcode = 3;
// Operands of the |opcode| instruction.
repeated InstructionOperand operand = 4;
}
message TransformationAddTypeArray {
// Adds an array type of the given element type and size to the module

View File

@@ -30,6 +30,7 @@
#include "source/fuzz/transformation_add_global_variable.h"
#include "source/fuzz/transformation_add_local_variable.h"
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
#include "source/fuzz/transformation_add_spec_constant_op.h"
#include "source/fuzz/transformation_add_type_array.h"
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
@@ -110,6 +111,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
kAddNoContractionDecoration:
return MakeUnique<TransformationAddNoContractionDecoration>(
message.add_no_contraction_decoration());
case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
return MakeUnique<TransformationAddSpecConstantOp>(
message.add_spec_constant_op());
case protobufs::Transformation::TransformationCase::kAddTypeArray:
return MakeUnique<TransformationAddTypeArray>(message.add_type_array());
case protobufs::Transformation::TransformationCase::kAddTypeBoolean:

View File

@@ -105,20 +105,8 @@ void TransformationAddGlobalVariable::Apply(
input_operands));
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(ir_context)) {
// Conservatively add this global to the interface of every entry point in
// the module. This means that the global is available for other
// transformations to use.
//
// A downside of this is that the global will be in the interface even if it
// ends up never being used.
//
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
// this if a more thorough approach to entry point interfaces is taken.
for (auto& entry_point : ir_context->module()->entry_points()) {
entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
}
}
fuzzerutil::AddVariableIdToEntryPointInterfaces(ir_context,
message_.fresh_id());
if (message_.value_is_irrelevant()) {
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
@@ -137,22 +125,5 @@ protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const {
return result;
}
bool TransformationAddGlobalVariable::
GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
opt::IRContext* ir_context) {
// TODO(afd): We capture the universal environments for which this requirement
// holds. The check should be refined on demand for other target
// environments.
switch (ir_context->grammar().target_env()) {
case SPV_ENV_UNIVERSAL_1_0:
case SPV_ENV_UNIVERSAL_1_1:
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_UNIVERSAL_1_3:
return false;
default:
return true;
}
}
} // namespace fuzz
} // namespace spvtools

View File

@@ -58,12 +58,6 @@ class TransformationAddGlobalVariable : public Transformation {
protobufs::Transformation ToMessage() const override;
private:
// Returns true if and only if the SPIR-V version being used requires that
// global variables accessed in the static call graph of an entry point need
// to be listed in that entry point's interface.
static bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
opt::IRContext* ir_context);
protobufs::TransformationAddGlobalVariable message_;
};

View File

@@ -0,0 +1,84 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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 <utility>
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_add_spec_constant_op.h"
namespace spvtools {
namespace fuzz {
TransformationAddSpecConstantOp::TransformationAddSpecConstantOp(
spvtools::fuzz::protobufs::TransformationAddSpecConstantOp message)
: message_(std::move(message)) {}
TransformationAddSpecConstantOp::TransformationAddSpecConstantOp(
uint32_t fresh_id, uint32_t type_id, SpvOp opcode,
const opt::Instruction::OperandList& operands) {
message_.set_fresh_id(fresh_id);
message_.set_type_id(type_id);
message_.set_opcode(opcode);
for (const auto& operand : operands) {
auto* op = message_.add_operand();
op->set_operand_type(operand.type);
for (auto word : operand.words) {
op->add_operand_data(word);
}
}
}
bool TransformationAddSpecConstantOp::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
auto clone = fuzzerutil::CloneIRContext(ir_context);
ApplyImpl(clone.get());
return fuzzerutil::IsValid(clone.get(),
transformation_context.GetValidatorOptions());
}
void TransformationAddSpecConstantOp::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
ApplyImpl(ir_context);
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
}
void TransformationAddSpecConstantOp::ApplyImpl(
opt::IRContext* ir_context) const {
opt::Instruction::OperandList operands = {
{SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, {message_.opcode()}}};
for (const auto& operand : message_.operand()) {
std::vector<uint32_t> words(operand.operand_data().begin(),
operand.operand_data().end());
operands.push_back({static_cast<spv_operand_type_t>(operand.operand_type()),
std::move(words)});
}
ir_context->AddGlobalValue(MakeUnique<opt::Instruction>(
ir_context, SpvOpSpecConstantOp, message_.type_id(), message_.fresh_id(),
std::move(operands)));
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
}
protobufs::Transformation TransformationAddSpecConstantOp::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_spec_constant_op() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@@ -0,0 +1,60 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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.
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_
#define SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
class TransformationAddSpecConstantOp : public Transformation {
public:
explicit TransformationAddSpecConstantOp(
protobufs::TransformationAddSpecConstantOp message);
TransformationAddSpecConstantOp(
uint32_t fresh_id, uint32_t type_id, SpvOp opcode,
const opt::Instruction::OperandList& operands);
// - |fresh_id| is a fresh result id in the module.
// - |type_id| is a valid result id of some OpType* instruction in the
// module. It is also a valid type id with respect to |opcode|.
// - |opcode| is one of the opcodes supported by OpSpecConstantOp.
// - |operands| are valid with respect to |opcode|
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// |%fresh_id = OpSpecConstantOp %type_id opcode operands...| is added to the
// module.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
private:
void ApplyImpl(opt::IRContext* ir_context) const;
protobufs::TransformationAddSpecConstantOp message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_

View File

@@ -109,6 +109,9 @@ void TransformationPushIdThroughVariable::Apply(
ir_context, SpvOpVariable, pointer_type_id, message_.variable_id(),
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}}})));
fuzzerutil::AddVariableIdToEntryPointInterfaces(ir_context,
message_.variable_id());
} else {
ir_context
->get_instr_block(

View File

@@ -101,6 +101,14 @@ void TransformationSetMemoryOperandsMask::Apply(
// Either add a new operand, if no mask operand was already present, or
// replace an existing mask operand.
if (original_mask_in_operand_index >= instruction->NumInOperands()) {
// Add first memory operand if it's missing.
if (message_.memory_operands_mask_index() == 1 &&
GetInOperandIndexForMask(*instruction, 0) >=
instruction->NumInOperands()) {
instruction->AddOperand(
{SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}});
}
instruction->AddOperand(
{SPV_OPERAND_TYPE_MEMORY_ACCESS, {message_.memory_operands_mask()}});
@@ -154,11 +162,26 @@ uint32_t TransformationSetMemoryOperandsMask::GetInOperandIndexForMask(
break;
}
// If we are looking for the input operand index of the first mask, return it.
// This will also return a correct value if the operand is missing.
if (mask_index == 0) {
return first_mask_in_operand_index;
}
assert(mask_index == 1 && "Memory operands mask index must be 0 or 1.");
// Memory mask operands are optional. Thus, if the second operand exists,
// its index will be >= |first_mask_in_operand_index + 1|. We can reason as
// follows to separate the cases where the index of the second operand is
// equal to |first_mask_in_operand_index + 1|:
// - If the first memory operand doesn't exist, its value is equal to None.
// This means that it doesn't have additional operands following it and the
// condition in the if statement below will be satisfied.
// - If the first memory operand exists and has no additional memory operands
// following it, the condition in the if statement below will be satisfied
// and we will return the correct value from the function.
if (first_mask_in_operand_index + 1 >= instruction.NumInOperands()) {
return first_mask_in_operand_index + 1;
}
// We are looking for the input operand index of the second mask. This is a
// little complicated because, depending on the contents of the first mask,
// there may be some input operands separating the two masks.