Updated spirv-tools.

This commit is contained in:
Бранимир Караџић
2019-10-28 19:53:59 -07:00
parent ac81eb1e9d
commit b8fa5e79b1
30 changed files with 1623 additions and 112 deletions

View File

@@ -1 +1 @@
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-140-g0dbd4e35"
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-144-g42f88523"

View File

@@ -68,7 +68,8 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_type_float.h
transformation_add_type_int.h
transformation_add_type_pointer.h
transformation_construct_composite.h
transformation_composite_construct.h
transformation_composite_extract.h
transformation_copy_object.h
transformation_move_block_down.h
transformation_replace_boolean_constant_with_constant_binary.h
@@ -119,7 +120,8 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_type_float.cpp
transformation_add_type_int.cpp
transformation_add_type_pointer.cpp
transformation_construct_composite.cpp
transformation_composite_construct.cpp
transformation_composite_extract.cpp
transformation_copy_object.cpp
transformation_move_block_down.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp

View File

@@ -283,7 +283,7 @@ bool FactManager::ConstantUniformFacts::AddFact(
auto composite_type =
should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
auto final_element_type_id = fuzzerutil::WalkCompositeIndices(
auto final_element_type_id = fuzzerutil::WalkCompositeTypeIndices(
context, composite_type,
fact.uniform_buffer_element_descriptor().index());
if (!final_element_type_id) {

View File

@@ -18,7 +18,7 @@
#include <memory>
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_construct_composite.h"
#include "source/fuzz/transformation_composite_construct.h"
#include "source/util/make_unique.h"
namespace spvtools {
@@ -140,7 +140,7 @@ void FuzzerPassConstructComposites::Apply() {
assert(constructor_arguments != nullptr);
// Make and apply a transformation.
TransformationConstructComposite transformation(
TransformationCompositeConstruct transformation(
chosen_composite_type, *constructor_arguments,
instruction_descriptor, GetFuzzerContext()->GetFreshId());
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&

View File

@@ -330,7 +330,7 @@ bool IsCompositeType(const opt::analysis::Type* type) {
type->AsVector());
}
uint32_t WalkCompositeIndices(
uint32_t WalkCompositeTypeIndices(
opt::IRContext* context, uint32_t base_object_type_id,
const google::protobuf::RepeatedField<google::protobuf::uint32>& indices) {
uint32_t sub_object_type_id = base_object_type_id;

View File

@@ -98,7 +98,7 @@ bool IsCompositeType(const opt::analysis::Type* type);
// |indices| is suitable for indexing into this type. Returns the id of the
// type of the final sub-object reached via the indices if they are valid, and
// 0 otherwise.
uint32_t WalkCompositeIndices(
uint32_t WalkCompositeTypeIndices(
opt::IRContext* context, uint32_t base_object_type_id,
const google::protobuf::RepeatedField<google::protobuf::uint32>& indices);

View File

@@ -187,11 +187,12 @@ message Transformation {
TransformationCopyObject copy_object = 13;
TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
TransformationSetSelectionControl set_selection_control = 15;
TransformationConstructComposite construct_composite = 16;
TransformationCompositeConstruct composite_construct = 16;
TransformationSetLoopControl set_loop_control = 17;
TransformationSetFunctionControl set_function_control = 18;
TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
TransformationCompositeExtract composite_extract = 21;
// Add additional option using the next available number.
}
}
@@ -325,24 +326,7 @@ message TransformationAddTypePointer {
}
message TransformationCopyObject {
// A transformation that introduces an OpCopyObject instruction to make a
// copy of an object.
// Id of the object to be copied
uint32 object = 1;
// A descriptor for an instruction in a block before which the new
// OpCopyObject instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 2;
// A fresh id for the copied object
uint32 fresh_id = 3;
}
message TransformationConstructComposite {
message TransformationCompositeConstruct {
// A transformation that introduces an OpCompositeConstruct instruction to
// make a composite object.
@@ -362,6 +346,43 @@ message TransformationConstructComposite {
}
message TransformationCompositeExtract {
// A transformation that adds an instruction to extract an element from a
// composite.
// A descriptor for an instruction in a block before which the new
// OpCompositeExtract instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 1;
// Result id for the extract operation.
uint32 fresh_id = 2;
// Id of the composite from which data is to be extracted.
uint32 composite_id = 3;
// Indices that indicate which part of the composite should be extracted.
repeated uint32 index = 4;
}
message TransformationCopyObject {
// A transformation that introduces an OpCopyObject instruction to make a
// copy of an object.
// Id of the object to be copied
uint32 object = 1;
// A descriptor for an instruction in a block before which the new
// OpCopyObject instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 2;
// A fresh id for the copied object
uint32 fresh_id = 3;
}
message TransformationMoveBlockDown {
// A transformation that moves a basic block to be one position lower in

View File

@@ -25,7 +25,8 @@
#include "source/fuzz/transformation_add_type_float.h"
#include "source/fuzz/transformation_add_type_int.h"
#include "source/fuzz/transformation_add_type_pointer.h"
#include "source/fuzz/transformation_construct_composite.h"
#include "source/fuzz/transformation_composite_construct.h"
#include "source/fuzz/transformation_composite_extract.h"
#include "source/fuzz/transformation_copy_object.h"
#include "source/fuzz/transformation_move_block_down.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
@@ -71,9 +72,12 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kAddTypePointer:
return MakeUnique<TransformationAddTypePointer>(
message.add_type_pointer());
case protobufs::Transformation::TransformationCase::kConstructComposite:
return MakeUnique<TransformationConstructComposite>(
message.construct_composite());
case protobufs::Transformation::TransformationCase::kCompositeConstruct:
return MakeUnique<TransformationCompositeConstruct>(
message.composite_construct());
case protobufs::Transformation::TransformationCase::kCompositeExtract:
return MakeUnique<TransformationCompositeExtract>(
message.composite_extract());
case protobufs::Transformation::TransformationCase::kCopyObject:
return MakeUnique<TransformationCopyObject>(message.copy_object());
case protobufs::Transformation::TransformationCase::kMoveBlockDown:

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/transformation_construct_composite.h"
#include "source/fuzz/transformation_composite_construct.h"
#include "source/fuzz/data_descriptor.h"
#include "source/fuzz/fuzzer_util.h"
@@ -22,11 +22,11 @@
namespace spvtools {
namespace fuzz {
TransformationConstructComposite::TransformationConstructComposite(
const protobufs::TransformationConstructComposite& message)
TransformationCompositeConstruct::TransformationCompositeConstruct(
const protobufs::TransformationCompositeConstruct& message)
: message_(message) {}
TransformationConstructComposite::TransformationConstructComposite(
TransformationCompositeConstruct::TransformationCompositeConstruct(
uint32_t composite_type_id, std::vector<uint32_t> component,
const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t fresh_id) {
@@ -39,7 +39,7 @@ TransformationConstructComposite::TransformationConstructComposite(
message_.set_fresh_id(fresh_id);
}
bool TransformationConstructComposite::IsApplicable(
bool TransformationCompositeConstruct::IsApplicable(
opt::IRContext* context, const FactManager& /*fact_manager*/) const {
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
// We require the id for the composite constructor to be unused.
@@ -112,7 +112,7 @@ bool TransformationConstructComposite::IsApplicable(
return true;
}
void TransformationConstructComposite::Apply(opt::IRContext* context,
void TransformationCompositeConstruct::Apply(opt::IRContext* context,
FactManager* fact_manager) const {
// Use the base and offset information from the transformation to determine
// where in the module a new instruction should be inserted.
@@ -174,7 +174,7 @@ void TransformationConstructComposite::Apply(opt::IRContext* context,
context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
bool TransformationConstructComposite::ComponentsForArrayConstructionAreOK(
bool TransformationCompositeConstruct::ComponentsForArrayConstructionAreOK(
opt::IRContext* context, const opt::analysis::Array& array_type) const {
if (array_type.length_info().words[0] !=
opt::analysis::Array::LengthInfo::kConstant) {
@@ -211,7 +211,7 @@ bool TransformationConstructComposite::ComponentsForArrayConstructionAreOK(
return true;
}
bool TransformationConstructComposite::ComponentsForMatrixConstructionAreOK(
bool TransformationCompositeConstruct::ComponentsForMatrixConstructionAreOK(
opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const {
if (static_cast<uint32_t>(message_.component().size()) !=
matrix_type.element_count()) {
@@ -237,7 +237,7 @@ bool TransformationConstructComposite::ComponentsForMatrixConstructionAreOK(
return true;
}
bool TransformationConstructComposite::ComponentsForStructConstructionAreOK(
bool TransformationCompositeConstruct::ComponentsForStructConstructionAreOK(
opt::IRContext* context, const opt::analysis::Struct& struct_type) const {
if (static_cast<uint32_t>(message_.component().size()) !=
struct_type.element_types().size()) {
@@ -265,7 +265,7 @@ bool TransformationConstructComposite::ComponentsForStructConstructionAreOK(
return true;
}
bool TransformationConstructComposite::ComponentsForVectorConstructionAreOK(
bool TransformationCompositeConstruct::ComponentsForVectorConstructionAreOK(
opt::IRContext* context, const opt::analysis::Vector& vector_type) const {
uint32_t base_element_count = 0;
auto element_type = vector_type.element_type();
@@ -295,9 +295,9 @@ bool TransformationConstructComposite::ComponentsForVectorConstructionAreOK(
return base_element_count == vector_type.element_count();
}
protobufs::Transformation TransformationConstructComposite::ToMessage() const {
protobufs::Transformation TransformationCompositeConstruct::ToMessage() const {
protobufs::Transformation result;
*result.mutable_construct_composite() = message_;
*result.mutable_composite_construct() = message_;
return result;
}

View File

@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_FUZZ_TRANSFORMATION_CONSTRUCT_COMPOSITE_H_
#define SOURCE_FUZZ_TRANSFORMATION_CONSTRUCT_COMPOSITE_H_
#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_
#define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@@ -23,12 +23,12 @@
namespace spvtools {
namespace fuzz {
class TransformationConstructComposite : public Transformation {
class TransformationCompositeConstruct : public Transformation {
public:
explicit TransformationConstructComposite(
const protobufs::TransformationConstructComposite& message);
explicit TransformationCompositeConstruct(
const protobufs::TransformationCompositeConstruct& message);
TransformationConstructComposite(
TransformationCompositeConstruct(
uint32_t composite_type_id, std::vector<uint32_t> component,
const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t fresh_id);
@@ -79,10 +79,10 @@ class TransformationConstructComposite : public Transformation {
bool ComponentsForVectorConstructionAreOK(
opt::IRContext* context, const opt::analysis::Vector& vector_type) const;
protobufs::TransformationConstructComposite message_;
protobufs::TransformationCompositeConstruct message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_CONSTRUCT_COMPOSITE_H_
#endif // SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_

View File

@@ -0,0 +1,123 @@
// 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_composite_extract.h"
#include <vector>
#include "source/fuzz/data_descriptor.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
namespace spvtools {
namespace fuzz {
TransformationCompositeExtract::TransformationCompositeExtract(
const spvtools::fuzz::protobufs::TransformationCompositeExtract& message)
: message_(message) {}
TransformationCompositeExtract::TransformationCompositeExtract(
const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t fresh_id, uint32_t composite_id, std::vector<uint32_t>&& index) {
*message_.mutable_instruction_to_insert_before() =
instruction_to_insert_before;
message_.set_fresh_id(fresh_id);
message_.set_composite_id(composite_id);
for (auto an_index : index) {
message_.add_index(an_index);
}
}
bool TransformationCompositeExtract::IsApplicable(
opt::IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) const {
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
return false;
}
auto instruction_to_insert_before =
FindInstruction(message_.instruction_to_insert_before(), context);
if (!instruction_to_insert_before) {
return false;
}
auto composite_instruction =
context->get_def_use_mgr()->GetDef(message_.composite_id());
if (!composite_instruction) {
return false;
}
if (auto block = context->get_instr_block(composite_instruction)) {
if (composite_instruction == instruction_to_insert_before ||
!context->GetDominatorAnalysis(block->GetParent())
->Dominates(composite_instruction, instruction_to_insert_before)) {
return false;
}
}
assert(composite_instruction->type_id() &&
"An instruction in a block cannot have a result id but no type id.");
auto composite_type =
context->get_type_mgr()->GetType(composite_instruction->type_id());
if (!composite_type) {
return false;
}
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
SpvOpCompositeExtract, instruction_to_insert_before)) {
return false;
}
return fuzzerutil::WalkCompositeTypeIndices(
context, composite_instruction->type_id(), message_.index()) != 0;
}
void TransformationCompositeExtract::Apply(
opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
opt::Instruction::OperandList extract_operands;
extract_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.composite_id()}});
for (auto an_index : message_.index()) {
extract_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {an_index}});
}
auto composite_instruction =
context->get_def_use_mgr()->GetDef(message_.composite_id());
auto extracted_type = fuzzerutil::WalkCompositeTypeIndices(
context, composite_instruction->type_id(), message_.index());
FindInstruction(message_.instruction_to_insert_before(), context)
->InsertBefore(MakeUnique<opt::Instruction>(
context, SpvOpCompositeExtract, extracted_type, message_.fresh_id(),
extract_operands));
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
// Add the fact that the id storing the extracted element is synonymous with
// the index into the structure.
std::vector<uint32_t> indices;
for (auto an_index : message_.index()) {
indices.push_back(an_index);
}
protobufs::DataDescriptor data_descriptor_for_extracted_element =
MakeDataDescriptor(message_.composite_id(), std::move(indices), 1);
protobufs::DataDescriptor data_descriptor_for_result_id =
MakeDataDescriptor(message_.fresh_id(), {}, 1);
fact_manager->AddFactDataSynonym(data_descriptor_for_extracted_element,
data_descriptor_for_result_id);
}
protobufs::Transformation TransformationCompositeExtract::ToMessage() const {
protobufs::Transformation result;
*result.mutable_composite_extract() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@@ -0,0 +1,63 @@
// 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.
#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_
#define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
class TransformationCompositeExtract : public Transformation {
public:
explicit TransformationCompositeExtract(
const protobufs::TransformationCompositeExtract& message);
TransformationCompositeExtract(
const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t fresh_id, uint32_t composite_id, std::vector<uint32_t>&& index);
// - |message_.fresh_id| must be available
// - |message_.instruction_to_insert_before| must identify an instruction
// before which it is valid to place an OpCompositeExtract
// - |message_.composite_id| must be the id of an instruction that defines
// a composite object, and this id must be available at the instruction
// identified by |message_.instruction_to_insert_before|
// - |message_.index| must be a suitable set of indices for
// |message_.composite_id|, i.e. it must be possible to follow this chain
// of indices to reach a sub-object of |message_.composite_id|
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// Adds an OpCompositeConstruct instruction before the instruction identified
// by |message_.instruction_to_insert_before|, that extracts from
// |message_.composite_id| via indices |message_.index| into
// |message_.fresh_id|. Generates a data synonym fact relating
// |message_.fresh_id| to the extracted element.
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationCompositeExtract message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_

View File

@@ -275,22 +275,27 @@ bool TransformationReplaceIdWithSynonym::ReplacingUseWithSynonymIsOk(
// We now need to check that replacing the use with the synonym will respect
// dominance rules - i.e. the synonym needs to dominate the use.
auto dominator_analysis = context->GetDominatorAnalysis(
context->get_instr_block(use_instruction)->GetParent());
if (use_instruction->opcode() == SpvOpPhi) {
// In the case where the use is an operand to OpPhi, it is actually the
// *parent* block associated with the operand that must be dominated by the
// synonym.
auto parent_block =
use_instruction->GetSingleWordInOperand(use_in_operand_index + 1);
if (!dominator_analysis->Dominates(
context->get_instr_block(defining_instruction)->id(),
parent_block)) {
// This is only relevant if the defining instruction is in a block; if it is
// not in a block then it is at global scope, and so replacing the use with it
// is fine.
if (context->get_instr_block(defining_instruction)) {
auto dominator_analysis = context->GetDominatorAnalysis(
context->get_instr_block(use_instruction)->GetParent());
if (use_instruction->opcode() == SpvOpPhi) {
// In the case where the use is an operand to OpPhi, it is actually the
// *parent* block associated with the operand that must be dominated by
// the synonym.
auto parent_block =
use_instruction->GetSingleWordInOperand(use_in_operand_index + 1);
if (!dominator_analysis->Dominates(
context->get_instr_block(defining_instruction)->id(),
parent_block)) {
return false;
}
} else if (!dominator_analysis->Dominates(defining_instruction,
use_instruction)) {
return false;
}
} else if (!dominator_analysis->Dominates(defining_instruction,
use_instruction)) {
return false;
}
return true;
}

View File

@@ -18,6 +18,7 @@
#include <memory>
#include <utility>
#include "ir_builder.h"
#include "source/latest_version_glsl_std_450_header.h"
#include "source/opt/ir_context.h"
@@ -1239,6 +1240,117 @@ FoldingRule MergeSubSubArithmetic() {
};
}
// Helper function for MergeGenericAddSubArithmetic. If |addend| and
// subtrahend of |sub| is the same, merge to copy of minuend of |sub|.
bool MergeGenericAddendSub(uint32_t addend, uint32_t sub, Instruction* inst) {
IRContext* context = inst->context();
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
Instruction* sub_inst = def_use_mgr->GetDef(sub);
if (sub_inst->opcode() != SpvOpFSub && sub_inst->opcode() != SpvOpISub)
return false;
if (sub_inst->opcode() == SpvOpFSub &&
!sub_inst->IsFloatingPointFoldingAllowed())
return false;
if (addend != sub_inst->GetSingleWordInOperand(1)) return false;
inst->SetOpcode(SpvOpCopyObject);
inst->SetInOperands(
{{SPV_OPERAND_TYPE_ID, {sub_inst->GetSingleWordInOperand(0)}}});
context->UpdateDefUse(inst);
return true;
}
// Folds addition of a subtraction where the subtrahend is equal to the
// other addend. Return a copy of the minuend. Accepts generic (const and
// non-const) operands.
// Cases:
// (a - b) + b = a
// b + (a - b) = a
FoldingRule MergeGenericAddSubArithmetic() {
return [](IRContext* context, Instruction* inst,
const std::vector<const analysis::Constant*>&) {
assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
const analysis::Type* type =
context->get_type_mgr()->GetType(inst->type_id());
bool uses_float = HasFloatingPoint(type);
if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
uint32_t width = ElementWidth(type);
if (width != 32 && width != 64) return false;
uint32_t add_op0 = inst->GetSingleWordInOperand(0);
uint32_t add_op1 = inst->GetSingleWordInOperand(1);
if (MergeGenericAddendSub(add_op0, add_op1, inst)) return true;
return MergeGenericAddendSub(add_op1, add_op0, inst);
};
}
// Helper function for FactorAddMuls. If |factor0_0| is the same as |factor1_0|,
// generate |factor0_0| * (|factor0_1| + |factor1_1|).
bool FactorAddMulsOpnds(uint32_t factor0_0, uint32_t factor0_1,
uint32_t factor1_0, uint32_t factor1_1,
Instruction* inst) {
IRContext* context = inst->context();
if (factor0_0 != factor1_0) return false;
InstructionBuilder ir_builder(
context, inst,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
Instruction* new_add_inst = ir_builder.AddBinaryOp(
inst->type_id(), inst->opcode(), factor0_1, factor1_1);
inst->SetOpcode(inst->opcode() == SpvOpFAdd ? SpvOpFMul : SpvOpIMul);
inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {factor0_0}},
{SPV_OPERAND_TYPE_ID, {new_add_inst->result_id()}}});
context->UpdateDefUse(inst);
return true;
}
// Perform the following factoring identity, handling all operand order
// combinations: (a * b) + (a * c) = a * (b + c)
FoldingRule FactorAddMuls() {
return [](IRContext* context, Instruction* inst,
const std::vector<const analysis::Constant*>&) {
assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
const analysis::Type* type =
context->get_type_mgr()->GetType(inst->type_id());
bool uses_float = HasFloatingPoint(type);
if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
uint32_t add_op0 = inst->GetSingleWordInOperand(0);
Instruction* add_op0_inst = def_use_mgr->GetDef(add_op0);
if (add_op0_inst->opcode() != SpvOpFMul &&
add_op0_inst->opcode() != SpvOpIMul)
return false;
uint32_t add_op1 = inst->GetSingleWordInOperand(1);
Instruction* add_op1_inst = def_use_mgr->GetDef(add_op1);
if (add_op1_inst->opcode() != SpvOpFMul &&
add_op1_inst->opcode() != SpvOpIMul)
return false;
// Only perform this optimization if both of the muls only have one use.
// Otherwise this is a deoptimization in size and performance.
if (def_use_mgr->NumUses(add_op0_inst) > 1) return false;
if (def_use_mgr->NumUses(add_op1_inst) > 1) return false;
if (add_op0_inst->opcode() == SpvOpFMul &&
(!add_op0_inst->IsFloatingPointFoldingAllowed() ||
!add_op1_inst->IsFloatingPointFoldingAllowed()))
return false;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
// Check if operand i in add_op0_inst matches operand j in add_op1_inst.
if (FactorAddMulsOpnds(add_op0_inst->GetSingleWordInOperand(i),
add_op0_inst->GetSingleWordInOperand(1 - i),
add_op1_inst->GetSingleWordInOperand(j),
add_op1_inst->GetSingleWordInOperand(1 - j),
inst))
return true;
}
}
return false;
};
}
FoldingRule IntMultipleBy1() {
return [](IRContext*, Instruction* inst,
const std::vector<const analysis::Constant*>& constants) {
@@ -2226,6 +2338,8 @@ void FoldingRules::AddFoldingRules() {
rules_[SpvOpFAdd].push_back(MergeAddNegateArithmetic());
rules_[SpvOpFAdd].push_back(MergeAddAddArithmetic());
rules_[SpvOpFAdd].push_back(MergeAddSubArithmetic());
rules_[SpvOpFAdd].push_back(MergeGenericAddSubArithmetic());
rules_[SpvOpFAdd].push_back(FactorAddMuls());
rules_[SpvOpFDiv].push_back(RedundantFDiv());
rules_[SpvOpFDiv].push_back(ReciprocalFDiv());
@@ -2251,6 +2365,8 @@ void FoldingRules::AddFoldingRules() {
rules_[SpvOpIAdd].push_back(MergeAddNegateArithmetic());
rules_[SpvOpIAdd].push_back(MergeAddAddArithmetic());
rules_[SpvOpIAdd].push_back(MergeAddSubArithmetic());
rules_[SpvOpIAdd].push_back(MergeGenericAddSubArithmetic());
rules_[SpvOpIAdd].push_back(FactorAddMuls());
rules_[SpvOpIMul].push_back(IntMultipleBy1());
rules_[SpvOpIMul].push_back(MergeMulMulArithmetic());

View File

@@ -32,6 +32,18 @@ Pass::Status SimplificationPass::Process() {
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
}
void SimplificationPass::AddNewOperands(
Instruction* folded_inst, std::unordered_set<Instruction*>* inst_seen,
std::vector<Instruction*>* work_list) {
analysis::DefUseManager* def_use_mgr = get_def_use_mgr();
folded_inst->ForEachInId(
[&inst_seen, &def_use_mgr, &work_list](uint32_t* iid) {
Instruction* iid_inst = def_use_mgr->GetDef(*iid);
if (!inst_seen->insert(iid_inst).second) return;
work_list->push_back(iid_inst);
});
}
bool SimplificationPass::SimplifyFunction(Function* function) {
bool modified = false;
// Phase 1: Traverse all instructions in dominance order.
@@ -44,13 +56,15 @@ bool SimplificationPass::SimplifyFunction(Function* function) {
std::unordered_set<Instruction*> process_phis;
std::unordered_set<Instruction*> inst_to_kill;
std::unordered_set<Instruction*> in_work_list;
std::unordered_set<Instruction*> inst_seen;
const InstructionFolder& folder = context()->get_instruction_folder();
cfg()->ForEachBlockInReversePostOrder(
function->entry().get(),
[&modified, &process_phis, &work_list, &in_work_list, &inst_to_kill,
&folder, this](BasicBlock* bb) {
&folder, &inst_seen, this](BasicBlock* bb) {
for (Instruction* inst = &*bb->begin(); inst; inst = inst->NextNode()) {
inst_seen.insert(inst);
if (inst->opcode() == SpvOpPhi) {
process_phis.insert(inst);
}
@@ -70,6 +84,9 @@ bool SimplificationPass::SimplifyFunction(Function* function) {
work_list.push_back(use);
}
});
AddNewOperands(inst, &inst_seen, &work_list);
if (inst->opcode() == SpvOpCopyObject) {
context()->ReplaceAllUsesWithPredicate(
inst->result_id(), inst->GetSingleWordInOperand(0),
@@ -97,6 +114,7 @@ bool SimplificationPass::SimplifyFunction(Function* function) {
for (size_t i = 0; i < work_list.size(); ++i) {
Instruction* inst = work_list[i];
in_work_list.erase(inst);
inst_seen.insert(inst);
bool is_foldable_copy =
inst->opcode() == SpvOpCopyObject &&
@@ -114,6 +132,8 @@ bool SimplificationPass::SimplifyFunction(Function* function) {
}
});
AddNewOperands(inst, &inst_seen, &work_list);
if (inst->opcode() == SpvOpCopyObject) {
context()->ReplaceAllUsesWithPredicate(
inst->result_id(), inst->GetSingleWordInOperand(0),

View File

@@ -42,6 +42,14 @@ class SimplificationPass : public Pass {
// instruction in |function| until nothing else in the function can be
// simplified.
bool SimplifyFunction(Function* function);
// FactorAddMul can create |folded_inst| Mul of new Add. If Mul, push any Add
// operand not in |seen_inst| into |worklist|. This is heavily restricted to
// improve compile time but can be expanded for future simplifications which
// simiarly create new operations.
void AddNewOperands(Instruction* folded_inst,
std::unordered_set<Instruction*>* inst_seen,
std::vector<Instruction*>* work_list);
};
} // namespace opt

View File

@@ -31,7 +31,8 @@ if (${SPIRV_BUILD_FUZZER})
transformation_add_type_float_test.cpp
transformation_add_type_int_test.cpp
transformation_add_type_pointer_test.cpp
transformation_construct_composite_test.cpp
transformation_composite_construct_test.cpp
transformation_composite_extract_test.cpp
transformation_copy_object_test.cpp
transformation_move_block_down_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/transformation_construct_composite.h"
#include "source/fuzz/transformation_composite_construct.h"
#include "source/fuzz/data_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
@@ -36,7 +36,7 @@ bool SynonymFactHolds(const FactManager& fact_manager, uint32_t id,
}) != synonyms.end();
}
TEST(TransformationConstructCompositeTest, ConstructArrays) {
TEST(TransformationCompositeConstructTest, ConstructArrays) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -146,11 +146,11 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
FactManager fact_manager;
// Make a vec2[3]
TransformationConstructComposite make_vec2_array_length_3(
TransformationCompositeConstruct make_vec2_array_length_3(
37, {41, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
200);
// Bad: there are too many components
TransformationConstructComposite make_vec2_array_length_3_bad(
TransformationCompositeConstruct make_vec2_array_length_3_bad(
37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
200);
ASSERT_TRUE(
@@ -164,10 +164,10 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 27, 200, {2}));
// Make a float[2]
TransformationConstructComposite make_float_array_length_2(
TransformationCompositeConstruct make_float_array_length_2(
9, {24, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
// Bad: %41 does not have type float
TransformationConstructComposite make_float_array_length_2_bad(
TransformationCompositeConstruct make_float_array_length_2_bad(
9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
ASSERT_TRUE(
make_float_array_length_2.IsApplicable(context.get(), fact_manager));
@@ -179,11 +179,11 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 40, 201, {1}));
// Make a bool[3]
TransformationConstructComposite make_bool_array_length_3(
TransformationCompositeConstruct make_bool_array_length_3(
47, {33, 50, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
202);
// Bad: %54 is not available at the desired program point.
TransformationConstructComposite make_bool_array_length_3_bad(
TransformationCompositeConstruct make_bool_array_length_3_bad(
47, {33, 54, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
202);
ASSERT_TRUE(
@@ -197,10 +197,10 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 50, 202, {2}));
// make a uvec3[2][2]
TransformationConstructComposite make_uvec3_array_length_2_2(
TransformationCompositeConstruct make_uvec3_array_length_2_2(
58, {69, 100}, MakeInstructionDescriptor(64, SpvOpStore, 0), 203);
// Bad: Skip count 100 is too large.
TransformationConstructComposite make_uvec3_array_length_2_2_bad(
TransformationCompositeConstruct make_uvec3_array_length_2_2_bad(
58, {33, 54}, MakeInstructionDescriptor(64, SpvOpStore, 100), 203);
ASSERT_TRUE(
make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager));
@@ -319,7 +319,7 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationConstructCompositeTest, ConstructMatrices) {
TEST(TransformationCompositeConstructTest, ConstructMatrices) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -399,10 +399,10 @@ TEST(TransformationConstructCompositeTest, ConstructMatrices) {
FactManager fact_manager;
// make a mat3x4
TransformationConstructComposite make_mat34(
TransformationCompositeConstruct make_mat34(
32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
// Bad: %35 is mat4x3, not mat3x4.
TransformationConstructComposite make_mat34_bad(
TransformationCompositeConstruct make_mat34_bad(
35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
ASSERT_TRUE(make_mat34.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_mat34_bad.IsApplicable(context.get(), fact_manager));
@@ -413,10 +413,10 @@ TEST(TransformationConstructCompositeTest, ConstructMatrices) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 31, 200, {2}));
// make a mat4x3
TransformationConstructComposite make_mat43(
TransformationCompositeConstruct make_mat43(
35, {11, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
// Bad: %25 does not match the matrix's column type.
TransformationConstructComposite make_mat43_bad(
TransformationCompositeConstruct make_mat43_bad(
35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager));
@@ -503,7 +503,7 @@ TEST(TransformationConstructCompositeTest, ConstructMatrices) {
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationConstructCompositeTest, ConstructStructs) {
TEST(TransformationCompositeConstructTest, ConstructStructs) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -600,10 +600,10 @@ TEST(TransformationConstructCompositeTest, ConstructStructs) {
FactManager fact_manager;
// make an Inner
TransformationConstructComposite make_inner(
TransformationCompositeConstruct make_inner(
9, {25, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
// Bad: Too few fields to make the struct.
TransformationConstructComposite make_inner_bad(
TransformationCompositeConstruct make_inner_bad(
9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
ASSERT_TRUE(make_inner.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_inner_bad.IsApplicable(context.get(), fact_manager));
@@ -613,11 +613,11 @@ TEST(TransformationConstructCompositeTest, ConstructStructs) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 19, 200, {1}));
// make an Outer
TransformationConstructComposite make_outer(
TransformationCompositeConstruct make_outer(
33, {46, 200, 56}, MakeInstructionDescriptor(200, SpvOpAccessChain, 0),
201);
// Bad: %200 is not available at the desired program point.
TransformationConstructComposite make_outer_bad(
TransformationCompositeConstruct make_outer_bad(
33, {46, 200, 56},
MakeInstructionDescriptor(200, SpvOpCompositeConstruct, 0), 201);
ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager));
@@ -721,7 +721,7 @@ TEST(TransformationConstructCompositeTest, ConstructStructs) {
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationConstructCompositeTest, ConstructVectors) {
TEST(TransformationCompositeConstructTest, ConstructVectors) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -913,10 +913,10 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
FactManager fact_manager;
TransformationConstructComposite make_vec2(
TransformationCompositeConstruct make_vec2(
7, {17, 11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
// Bad: not enough data for a vec2
TransformationConstructComposite make_vec2_bad(
TransformationCompositeConstruct make_vec2_bad(
7, {11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
ASSERT_TRUE(make_vec2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_vec2_bad.IsApplicable(context.get(), fact_manager));
@@ -925,11 +925,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 17, 200, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 200, {1}));
TransformationConstructComposite make_vec3(
TransformationCompositeConstruct make_vec3(
25, {12, 32}, MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0),
201);
// Bad: too much data for a vec3
TransformationConstructComposite make_vec3_bad(
TransformationCompositeConstruct make_vec3_bad(
25, {12, 32, 32},
MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), 201);
ASSERT_TRUE(make_vec3.IsApplicable(context.get(), fact_manager));
@@ -939,11 +939,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 12, 201, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 32, 201, {2}));
TransformationConstructComposite make_vec4(
TransformationCompositeConstruct make_vec4(
44, {32, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
202);
// Bad: id 48 is not available at the insertion points
TransformationConstructComposite make_vec4_bad(
TransformationCompositeConstruct make_vec4_bad(
44, {48, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
202);
ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager));
@@ -955,10 +955,10 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 10, 202, {2}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 202, {3}));
TransformationConstructComposite make_ivec2(
TransformationCompositeConstruct make_ivec2(
51, {126, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
// Bad: if 128 is not available at the instruction that defines 128
TransformationConstructComposite make_ivec2_bad(
TransformationCompositeConstruct make_ivec2_bad(
51, {128, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_ivec2_bad.IsApplicable(context.get(), fact_manager));
@@ -967,11 +967,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 126, 203, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 120, 203, {1}));
TransformationConstructComposite make_ivec3(
TransformationCompositeConstruct make_ivec3(
114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
204);
// Bad because 1300 is not an id
TransformationConstructComposite make_ivec3_bad(
TransformationCompositeConstruct make_ivec3_bad(
114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
204);
ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager));
@@ -982,11 +982,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 204, {1}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 204, {2}));
TransformationConstructComposite make_ivec4(
TransformationCompositeConstruct make_ivec4(
122, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
205);
// Bad because 86 is the wrong type.
TransformationConstructComposite make_ivec4_bad(
TransformationCompositeConstruct make_ivec4_bad(
86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
205);
ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager));
@@ -998,9 +998,9 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {2}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {3}));
TransformationConstructComposite make_uvec2(
TransformationCompositeConstruct make_uvec2(
86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206);
TransformationConstructComposite make_uvec2_bad(
TransformationCompositeConstruct make_uvec2_bad(
86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206);
ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager));
@@ -1009,10 +1009,10 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 206, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 38, 206, {1}));
TransformationConstructComposite make_uvec3(
TransformationCompositeConstruct make_uvec3(
59, {14, 18, 136}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
// Bad because 1300 is not an id
TransformationConstructComposite make_uvec3_bad(
TransformationCompositeConstruct make_uvec3_bad(
59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_uvec3_bad.IsApplicable(context.get(), fact_manager));
@@ -1022,11 +1022,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 207, {1}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 207, {2}));
TransformationConstructComposite make_uvec4(
TransformationCompositeConstruct make_uvec4(
131, {14, 18, 136, 136},
MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
// Bad because 86 is the wrong type.
TransformationConstructComposite make_uvec4_bad(
TransformationCompositeConstruct make_uvec4_bad(
86, {14, 18, 136, 136},
MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager));
@@ -1038,7 +1038,7 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {2}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {3}));
TransformationConstructComposite make_bvec2(
TransformationCompositeConstruct make_bvec2(
102,
{
111,
@@ -1046,7 +1046,7 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
},
MakeInstructionDescriptor(75, SpvOpAccessChain, 0), 209);
// Bad because 0 is not a valid base instruction id
TransformationConstructComposite make_bvec2_bad(
TransformationCompositeConstruct make_bvec2_bad(
102,
{
111,
@@ -1060,10 +1060,10 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 111, 209, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 41, 209, {1}));
TransformationConstructComposite make_bvec3(
TransformationCompositeConstruct make_bvec3(
93, {108, 73}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
// Bad because there are too many components for a bvec3
TransformationConstructComposite make_bvec3_bad(
TransformationCompositeConstruct make_bvec3_bad(
93, {108, 108}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_bvec3_bad.IsApplicable(context.get(), fact_manager));
@@ -1072,10 +1072,10 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 108, 210, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 73, 210, {2}));
TransformationConstructComposite make_bvec4(
TransformationCompositeConstruct make_bvec4(
70, {108, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
// Bad because 21 is a type, not a result id
TransformationConstructComposite make_bvec4_bad(
TransformationCompositeConstruct make_bvec4_bad(
70, {21, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_bvec4_bad.IsApplicable(context.get(), fact_manager));

View File

@@ -0,0 +1,393 @@
// 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_composite_extract.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
bool IsSynonymous(const FactManager& fact_manager, uint32_t id,
uint32_t composite_id, std::vector<uint32_t>&& indices) {
protobufs::DataDescriptor data_descriptor =
MakeDataDescriptor(composite_id, std::move(indices), 1);
return fact_manager.GetSynonymsForId(id).count(&data_descriptor) == 1;
}
TEST(TransformationCompositeExtractTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpName %17 "FunnyPoint"
OpMemberName %17 0 "x"
OpMemberName %17 1 "y"
OpMemberName %17 2 "z"
OpName %19 "p"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%12 = OpTypeBool
%16 = OpTypeFloat 32
%17 = OpTypeStruct %16 %16 %6
%81 = OpTypeStruct %17 %16
%18 = OpTypePointer Function %17
%20 = OpConstant %6 0
%23 = OpTypePointer Function %16
%26 = OpConstant %6 1
%30 = OpConstant %6 2
%80 = OpUndef %16
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%19 = OpVariable %18 Function
%9 = OpLoad %6 %8
%11 = OpLoad %6 %10
%100 = OpCompositeConstruct %17 %80 %80 %26
%104 = OpCompositeConstruct %81 %100 %80
%13 = OpIEqual %12 %9 %11
OpSelectionMerge %15 None
OpBranchConditional %13 %14 %25
%14 = OpLabel
%21 = OpLoad %6 %8
%22 = OpConvertSToF %16 %21
%101 = OpCompositeConstruct %17 %22 %80 %30
%24 = OpAccessChain %23 %19 %20
OpStore %24 %22
OpBranch %15
%25 = OpLabel
%27 = OpLoad %6 %10
%28 = OpConvertSToF %16 %27
%102 = OpCompositeConstruct %17 %80 %28 %27
%29 = OpAccessChain %23 %19 %26
OpStore %29 %28
OpBranch %15
%15 = OpLabel
%31 = OpAccessChain %23 %19 %20
%32 = OpLoad %16 %31
%33 = OpAccessChain %23 %19 %26
%34 = OpLoad %16 %33
%103 = OpCompositeConstruct %17 %34 %32 %9
%35 = OpFAdd %16 %32 %34
%36 = OpConvertFToS %6 %35
%37 = OpAccessChain %7 %19 %30
OpStore %37 %36
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;
// Instruction does not exist.
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 101, {0})
.IsApplicable(context.get(), fact_manager));
// Id for composite is not a composite.
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 27, {})
.IsApplicable(context.get(), fact_manager));
// Composite does not dominate instruction being inserted before.
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 101, {0})
.IsApplicable(context.get(), fact_manager));
// Too many indices for extraction from struct composite.
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 200, 101, {0, 0})
.IsApplicable(context.get(), fact_manager));
// Too many indices for extraction from struct composite.
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 0, 0})
.IsApplicable(context.get(), fact_manager));
// Out of bounds index for extraction from struct composite.
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 3})
.IsApplicable(context.get(), fact_manager));
// Result id already used.
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(35, SpvOpFAdd, 0), 80, 103, {0})
.IsApplicable(context.get(), fact_manager));
TransformationCompositeExtract transformation_1(
MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
ASSERT_TRUE(transformation_1.IsApplicable(context.get(), fact_manager));
transformation_1.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationCompositeExtract transformation_2(
MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 202, 104, {0, 2});
ASSERT_TRUE(transformation_2.IsApplicable(context.get(), fact_manager));
transformation_2.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationCompositeExtract transformation_3(
MakeInstructionDescriptor(29, SpvOpAccessChain, 0), 203, 104, {0});
ASSERT_TRUE(transformation_3.IsApplicable(context.get(), fact_manager));
transformation_3.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationCompositeExtract transformation_4(
MakeInstructionDescriptor(24, SpvOpStore, 0), 204, 101, {0});
ASSERT_TRUE(transformation_4.IsApplicable(context.get(), fact_manager));
transformation_4.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationCompositeExtract transformation_5(
MakeInstructionDescriptor(29, SpvOpBranch, 0), 205, 102, {2});
ASSERT_TRUE(transformation_5.IsApplicable(context.get(), fact_manager));
transformation_5.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationCompositeExtract transformation_6(
MakeInstructionDescriptor(37, SpvOpReturn, 0), 206, 103, {1});
ASSERT_TRUE(transformation_6.IsApplicable(context.get(), fact_manager));
transformation_6.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(IsSynonymous(fact_manager, 201, 100, {2}));
ASSERT_TRUE(IsSynonymous(fact_manager, 202, 104, {0, 2}));
ASSERT_TRUE(IsSynonymous(fact_manager, 203, 104, {0}));
ASSERT_TRUE(IsSynonymous(fact_manager, 204, 101, {0}));
ASSERT_TRUE(IsSynonymous(fact_manager, 205, 102, {2}));
ASSERT_TRUE(IsSynonymous(fact_manager, 206, 103, {1}));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpName %17 "FunnyPoint"
OpMemberName %17 0 "x"
OpMemberName %17 1 "y"
OpMemberName %17 2 "z"
OpName %19 "p"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%12 = OpTypeBool
%16 = OpTypeFloat 32
%17 = OpTypeStruct %16 %16 %6
%81 = OpTypeStruct %17 %16
%18 = OpTypePointer Function %17
%20 = OpConstant %6 0
%23 = OpTypePointer Function %16
%26 = OpConstant %6 1
%30 = OpConstant %6 2
%80 = OpUndef %16
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%19 = OpVariable %18 Function
%9 = OpLoad %6 %8
%11 = OpLoad %6 %10
%100 = OpCompositeConstruct %17 %80 %80 %26
%104 = OpCompositeConstruct %81 %100 %80
%13 = OpIEqual %12 %9 %11
OpSelectionMerge %15 None
OpBranchConditional %13 %14 %25
%14 = OpLabel
%21 = OpLoad %6 %8
%22 = OpConvertSToF %16 %21
%101 = OpCompositeConstruct %17 %22 %80 %30
%24 = OpAccessChain %23 %19 %20
%204 = OpCompositeExtract %16 %101 0
OpStore %24 %22
OpBranch %15
%25 = OpLabel
%27 = OpLoad %6 %10
%28 = OpConvertSToF %16 %27
%102 = OpCompositeConstruct %17 %80 %28 %27
%203 = OpCompositeExtract %17 %104 0
%29 = OpAccessChain %23 %19 %26
OpStore %29 %28
%205 = OpCompositeExtract %6 %102 2
OpBranch %15
%15 = OpLabel
%31 = OpAccessChain %23 %19 %20
%32 = OpLoad %16 %31
%33 = OpAccessChain %23 %19 %26
%34 = OpLoad %16 %33
%103 = OpCompositeConstruct %17 %34 %32 %9
%35 = OpFAdd %16 %32 %34
%201 = OpCompositeExtract %6 %100 2
%36 = OpConvertFToS %6 %35
%202 = OpCompositeExtract %6 %104 0 2
%37 = OpAccessChain %7 %19 %30
OpStore %37 %36
%206 = OpCompositeExtract %16 %103 1
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationCompositeExtractTest, IllegalInsertionPoints) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %51 %27
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %25 "buf"
OpMemberName %25 0 "value"
OpName %27 ""
OpName %51 "color"
OpMemberDecorate %25 0 Offset 0
OpDecorate %25 Block
OpDecorate %27 DescriptorSet 0
OpDecorate %27 Binding 0
OpDecorate %51 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%10 = OpConstant %6 0.300000012
%11 = OpConstant %6 0.400000006
%12 = OpConstant %6 0.5
%13 = OpConstant %6 1
%14 = OpConstantComposite %7 %10 %11 %12 %13
%15 = OpTypeInt 32 1
%18 = OpConstant %15 0
%25 = OpTypeStruct %6
%26 = OpTypePointer Uniform %25
%27 = OpVariable %26 Uniform
%28 = OpTypePointer Uniform %6
%32 = OpTypeBool
%103 = OpConstantTrue %32
%34 = OpConstant %6 0.100000001
%48 = OpConstant %15 1
%50 = OpTypePointer Output %7
%51 = OpVariable %50 Output
%100 = OpTypePointer Function %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%101 = OpVariable %100 Function
%102 = OpVariable %100 Function
OpBranch %19
%19 = OpLabel
%60 = OpPhi %7 %14 %5 %58 %20
%59 = OpPhi %15 %18 %5 %49 %20
%29 = OpAccessChain %28 %27 %18
%30 = OpLoad %6 %29
%31 = OpConvertFToS %15 %30
%33 = OpSLessThan %32 %59 %31
OpLoopMerge %21 %20 None
OpBranchConditional %33 %20 %21
%20 = OpLabel
%39 = OpCompositeExtract %6 %60 0
%40 = OpFAdd %6 %39 %34
%55 = OpCompositeInsert %7 %40 %60 0
%44 = OpCompositeExtract %6 %60 1
%45 = OpFSub %6 %44 %34
%58 = OpCompositeInsert %7 %45 %55 1
%49 = OpIAdd %15 %59 %48
OpBranch %19
%21 = OpLabel
OpStore %51 %60
OpSelectionMerge %105 None
OpBranchConditional %103 %104 %105
%104 = OpLabel
OpBranch %105
%105 = 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;
// Cannot insert before the OpVariables of a function.
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, {0})
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, {1})
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, {1})
.IsApplicable(context.get(), fact_manager));
// OK to insert right after the OpVariables.
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, {1})
.IsApplicable(context.get(), fact_manager));
// Cannot insert before the OpPhis of a block.
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(60, SpvOpPhi, 0), 200, 14, {2})
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(59, SpvOpPhi, 0), 200, 14, {3})
.IsApplicable(context.get(), fact_manager));
// OK to insert after the OpPhis.
ASSERT_TRUE(
TransformationCompositeExtract(
MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14, {3})
.IsApplicable(context.get(), fact_manager));
// Cannot insert before OpLoopMerge
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
200, 14, {3})
.IsApplicable(context.get(), fact_manager));
// Cannot insert before OpSelectionMerge
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
200, 14, {2})
.IsApplicable(context.get(), fact_manager));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -5763,6 +5763,132 @@ INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest,
4, true)
));
INSTANTIATE_TEST_SUITE_P(MergeGenericAddSub, MatchingInstructionFoldingTest,
::testing::Values(
// Test case 0: merge of add of sub
// (a - b) + b => a
InstructionFoldingCase<bool>(
Header() +
"; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
"; CHECK: %6 = OpCopyObject [[float]] %3\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var0 = OpVariable %_ptr_float Function\n" +
"%var1 = OpVariable %_ptr_float Function\n" +
"%3 = OpLoad %float %var0\n" +
"%4 = OpLoad %float %var1\n" +
"%5 = OpFSub %float %3 %4\n" +
"%6 = OpFAdd %float %5 %4\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
6, true),
// Test case 1: merge of add of sub
// b + (a - b) => a
InstructionFoldingCase<bool>(
Header() +
"; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
"; CHECK: %6 = OpCopyObject [[float]] %3\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var0 = OpVariable %_ptr_float Function\n" +
"%var1 = OpVariable %_ptr_float Function\n" +
"%3 = OpLoad %float %var0\n" +
"%4 = OpLoad %float %var1\n" +
"%5 = OpFSub %float %3 %4\n" +
"%6 = OpFAdd %float %4 %5\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
6, true)
));
INSTANTIATE_TEST_SUITE_P(FactorAddMul, MatchingInstructionFoldingTest,
::testing::Values(
// Test case 0: factor of add of muls
// (a * b) + (a * c) => a * (b + c)
InstructionFoldingCase<bool>(
Header() +
"; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
"; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" +
"; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var0 = OpVariable %_ptr_float Function\n" +
"%var1 = OpVariable %_ptr_float Function\n" +
"%var2 = OpVariable %_ptr_float Function\n" +
"%4 = OpLoad %float %var0\n" +
"%5 = OpLoad %float %var1\n" +
"%6 = OpLoad %float %var2\n" +
"%7 = OpFMul %float %6 %4\n" +
"%8 = OpFMul %float %6 %5\n" +
"%9 = OpFAdd %float %7 %8\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
9, true),
// Test case 1: factor of add of muls
// (b * a) + (a * c) => a * (b + c)
InstructionFoldingCase<bool>(
Header() +
"; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
"; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" +
"; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var0 = OpVariable %_ptr_float Function\n" +
"%var1 = OpVariable %_ptr_float Function\n" +
"%var2 = OpVariable %_ptr_float Function\n" +
"%4 = OpLoad %float %var0\n" +
"%5 = OpLoad %float %var1\n" +
"%6 = OpLoad %float %var2\n" +
"%7 = OpFMul %float %4 %6\n" +
"%8 = OpFMul %float %6 %5\n" +
"%9 = OpFAdd %float %7 %8\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
9, true),
// Test case 2: factor of add of muls
// (a * b) + (c * a) => a * (b + c)
InstructionFoldingCase<bool>(
Header() +
"; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
"; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" +
"; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var0 = OpVariable %_ptr_float Function\n" +
"%var1 = OpVariable %_ptr_float Function\n" +
"%var2 = OpVariable %_ptr_float Function\n" +
"%4 = OpLoad %float %var0\n" +
"%5 = OpLoad %float %var1\n" +
"%6 = OpLoad %float %var2\n" +
"%7 = OpFMul %float %6 %4\n" +
"%8 = OpFMul %float %5 %6\n" +
"%9 = OpFAdd %float %7 %8\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
9, true),
// Test case 3: factor of add of muls
// (b * a) + (c * a) => a * (b + c)
InstructionFoldingCase<bool>(
Header() +
"; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
"; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" +
"; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var0 = OpVariable %_ptr_float Function\n" +
"%var1 = OpVariable %_ptr_float Function\n" +
"%var2 = OpVariable %_ptr_float Function\n" +
"%4 = OpLoad %float %var0\n" +
"%5 = OpLoad %float %var1\n" +
"%6 = OpLoad %float %var2\n" +
"%7 = OpFMul %float %4 %6\n" +
"%8 = OpFMul %float %5 %6\n" +
"%9 = OpFAdd %float %7 %8\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
9, true)
));
INSTANTIATE_TEST_SUITE_P(MergeSubTest, MatchingInstructionFoldingTest,
::testing::Values(
// Test case 0: merge sub of negate

View File

@@ -71,6 +71,41 @@ TEST_F(SimplificationTest, StraightLineTest) {
SinglePassRunAndMatch<SimplificationPass>(text, false);
}
TEST_F(SimplificationTest, NewInstructionTest) {
// Testing that new instructions are simplified. Specifically,
// that the new add instruction generated by FactorAddMul is
// further simplified by MergeGenericAddSub.
const std::string text = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 430
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_int = OpTypePointer Function %int
; CHECK: [[mul:%[a-zA-Z_\d]+]] = OpIMul %int %13 %11
%main = OpFunction %void None %4
%7 = OpLabel
%8 = OpVariable %_ptr_int Function
%9 = OpVariable %_ptr_int Function
%10 = OpVariable %_ptr_int Function
%11 = OpLoad %int %8
%12 = OpLoad %int %9
%13 = OpLoad %int %10
%14 = OpISub %int %11 %12
%15 = OpIMul %int %13 %11
%16 = OpIMul %int %13 %12
%17 = OpIAdd %int %14 %15
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<SimplificationPass>(text, false);
}
TEST_F(SimplificationTest, AcrossBasicBlocks) {
// Testing that folding rules are combined across basic blocks.
const std::string text = R"(OpCapability Shader

View File

@@ -0,0 +1 @@
cache/

View File

@@ -0,0 +1,7 @@
# Visual Studio Code extension for SPIR-V disassembly files
This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V disassembly files (.spirv)
## Installing (macOS / Linux)
Simply run `install.sh`

26
3rdparty/spirv-tools/utils/vscode/install.sh vendored Executable file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Copyright (c) 2019 Google Inc.
#
# 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.
set -e # Fail on any error.
EXT_PATH=~/.vscode/extensions/google.spirvls-0.0.1
ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
go run ${ROOT_PATH}/src/tools/gen-grammar.go --cache ${ROOT_PATH}/cache --template ${ROOT_PATH}/spirv.json.tmpl --out ${ROOT_PATH}/spirv.json
mkdir -p ${EXT_PATH}
cp ${ROOT_PATH}/package.json ${EXT_PATH}
cp ${ROOT_PATH}/spirv.configuration.json ${EXT_PATH}
cp ${ROOT_PATH}/spirv.json ${EXT_PATH}

View File

@@ -0,0 +1,33 @@
{
"name": "spirv",
"description": "Language support for SPIR-V disassembly files",
"author": "Google",
"license": "Apache-2.0",
"version": "0.0.1",
"private": true,
"publisher": "Google",
"engines": {
"vscode": "^0.10.10"
},
"categories": [
"Programming Languages"
],
"contributes": {
"languages": [
{
"id": "spirv",
"configuration": "spirv.configuration.json",
"extensions": [
"spirv"
]
}
],
"grammars": [
{
"language": "spirv",
"scopeName": "source.spirv",
"path": "spirv.json"
}
]
}
}

View File

@@ -0,0 +1,6 @@
{
"comments": {
"lineComment": ";"
},
"wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)"
}

View File

@@ -0,0 +1,212 @@
{
"scopeName": "source.spirv",
"name": "SPIR-V",
"comment": "Generated by gen-grammar.go --template=../../spirv.json.tmpl --out=../../spirv.json. Do not modify this file directly.",
"patterns": [
{ "include": "#BitEnum_ImageOperands" },
{ "include": "#BitEnum_FPFastMathMode" },
{ "include": "#BitEnum_SelectionControl" },
{ "include": "#BitEnum_LoopControl" },
{ "include": "#BitEnum_FunctionControl" },
{ "include": "#BitEnum_MemorySemantics" },
{ "include": "#BitEnum_MemoryAccess" },
{ "include": "#BitEnum_KernelProfilingInfo" },
{ "include": "#ValueEnum_SourceLanguage" },
{ "include": "#ValueEnum_ExecutionModel" },
{ "include": "#ValueEnum_AddressingModel" },
{ "include": "#ValueEnum_MemoryModel" },
{ "include": "#ValueEnum_ExecutionMode" },
{ "include": "#ValueEnum_StorageClass" },
{ "include": "#ValueEnum_Dim" },
{ "include": "#ValueEnum_SamplerAddressingMode" },
{ "include": "#ValueEnum_SamplerFilterMode" },
{ "include": "#ValueEnum_ImageFormat" },
{ "include": "#ValueEnum_ImageChannelOrder" },
{ "include": "#ValueEnum_ImageChannelDataType" },
{ "include": "#ValueEnum_FPRoundingMode" },
{ "include": "#ValueEnum_LinkageType" },
{ "include": "#ValueEnum_AccessQualifier" },
{ "include": "#ValueEnum_FunctionParameterAttribute" },
{ "include": "#ValueEnum_Decoration" },
{ "include": "#ValueEnum_BuiltIn" },
{ "include": "#ValueEnum_Scope" },
{ "include": "#ValueEnum_GroupOperation" },
{ "include": "#ValueEnum_KernelEnqueueFlags" },
{ "include": "#ValueEnum_Capability" },
{ "include": "#opcode" },
{ "include": "#identifier" },
{ "include": "#number" },
{ "include": "#string" },
{ "include": "#comment" },
{ "include": "#operator" }
],
"repository": {
"BitEnum_ImageOperands": {
"match": "\\b(None|Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod|MakeTexelAvailable|MakeTexelAvailableKHR|MakeTexelVisible|MakeTexelVisibleKHR|NonPrivateTexel|NonPrivateTexelKHR|VolatileTexel|VolatileTexelKHR|SignExtend|ZeroExtend)\\b",
"name": "keyword.spirv"
},
"BitEnum_FPFastMathMode": {
"match": "\\b(None|NotNaN|NotInf|NSZ|AllowRecip|Fast)\\b",
"name": "keyword.spirv"
},
"BitEnum_SelectionControl": {
"match": "\\b(None|Flatten|DontFlatten)\\b",
"name": "keyword.spirv"
},
"BitEnum_LoopControl": {
"match": "\\b(None|Unroll|DontUnroll|DependencyInfinite|DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount)\\b",
"name": "keyword.spirv"
},
"BitEnum_FunctionControl": {
"match": "\\b(None|Inline|DontInline|Pure|Const)\\b",
"name": "keyword.spirv"
},
"BitEnum_MemorySemantics": {
"match": "\\b(Relaxed|None|Acquire|Release|AcquireRelease|SequentiallyConsistent|UniformMemory|SubgroupMemory|WorkgroupMemory|CrossWorkgroupMemory|AtomicCounterMemory|ImageMemory|OutputMemory|OutputMemoryKHR|MakeAvailable|MakeAvailableKHR|MakeVisible|MakeVisibleKHR|Volatile)\\b",
"name": "keyword.spirv"
},
"BitEnum_MemoryAccess": {
"match": "\\b(None|Volatile|Aligned|Nontemporal|MakePointerAvailable|MakePointerAvailableKHR|MakePointerVisible|MakePointerVisibleKHR|NonPrivatePointer|NonPrivatePointerKHR)\\b",
"name": "keyword.spirv"
},
"BitEnum_KernelProfilingInfo": {
"match": "\\b(None|CmdExecTime)\\b",
"name": "keyword.spirv"
},
"ValueEnum_SourceLanguage": {
"match": "\\b(Unknown|ESSL|GLSL|OpenCL_C|OpenCL_CPP|HLSL)\\b",
"name": "keyword.spirv"
},
"ValueEnum_ExecutionModel": {
"match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationNV|IntersectionNV|AnyHitNV|ClosestHitNV|MissNV|CallableNV)\\b",
"name": "keyword.spirv"
},
"ValueEnum_AddressingModel": {
"match": "\\b(Logical|Physical32|Physical64|PhysicalStorageBuffer64|PhysicalStorageBuffer64EXT)\\b",
"name": "keyword.spirv"
},
"ValueEnum_MemoryModel": {
"match": "\\b(Simple|GLSL450|OpenCL|Vulkan|VulkanKHR)\\b",
"name": "keyword.spirv"
},
"ValueEnum_ExecutionMode": {
"match": "\\b(Invocations|SpacingEqual|SpacingFractionalEven|SpacingFractionalOdd|VertexOrderCw|VertexOrderCcw|PixelCenterInteger|OriginUpperLeft|OriginLowerLeft|EarlyFragmentTests|PointMode|Xfb|DepthReplacing|DepthGreater|DepthLess|DepthUnchanged|LocalSize|LocalSizeHint|InputPoints|InputLines|InputLinesAdjacency|Triangles|InputTrianglesAdjacency|Quads|Isolines|OutputVertices|OutputPoints|OutputLineStrip|OutputTriangleStrip|VecTypeHint|ContractionOff|Initializer|Finalizer|SubgroupSize|SubgroupsPerWorkgroup|SubgroupsPerWorkgroupId|LocalSizeId|LocalSizeHintId|PostDepthCoverage|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|StencilRefReplacingEXT|OutputLinesNV|OutputPrimitivesNV|DerivativeGroupQuadsNV|DerivativeGroupLinearNV|OutputTrianglesNV|PixelInterlockOrderedEXT|PixelInterlockUnorderedEXT|SampleInterlockOrderedEXT|SampleInterlockUnorderedEXT|ShadingRateInterlockOrderedEXT|ShadingRateInterlockUnorderedEXT)\\b",
"name": "keyword.spirv"
},
"ValueEnum_StorageClass": {
"match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|CallableDataNV|IncomingCallableDataNV|RayPayloadNV|HitAttributeNV|IncomingRayPayloadNV|ShaderRecordBufferNV|PhysicalStorageBuffer|PhysicalStorageBufferEXT)\\b",
"name": "keyword.spirv"
},
"ValueEnum_Dim": {
"match": "\\b(1D|2D|3D|Cube|Rect|Buffer|SubpassData)\\b",
"name": "keyword.spirv"
},
"ValueEnum_SamplerAddressingMode": {
"match": "\\b(None|ClampToEdge|Clamp|Repeat|RepeatMirrored)\\b",
"name": "keyword.spirv"
},
"ValueEnum_SamplerFilterMode": {
"match": "\\b(Nearest|Linear)\\b",
"name": "keyword.spirv"
},
"ValueEnum_ImageFormat": {
"match": "\\b(Unknown|Rgba32f|Rgba16f|R32f|Rgba8|Rgba8Snorm|Rg32f|Rg16f|R11fG11fB10f|R16f|Rgba16|Rgb10A2|Rg16|Rg8|R16|R8|Rgba16Snorm|Rg16Snorm|Rg8Snorm|R16Snorm|R8Snorm|Rgba32i|Rgba16i|Rgba8i|R32i|Rg32i|Rg16i|Rg8i|R16i|R8i|Rgba32ui|Rgba16ui|Rgba8ui|R32ui|Rgb10a2ui|Rg32ui|Rg16ui|Rg8ui|R16ui|R8ui)\\b",
"name": "keyword.spirv"
},
"ValueEnum_ImageChannelOrder": {
"match": "\\b(R|A|RG|RA|RGB|RGBA|BGRA|ARGB|Intensity|Luminance|Rx|RGx|RGBx|Depth|DepthStencil|sRGB|sRGBx|sRGBA|sBGRA|ABGR)\\b",
"name": "keyword.spirv"
},
"ValueEnum_ImageChannelDataType": {
"match": "\\b(SnormInt8|SnormInt16|UnormInt8|UnormInt16|UnormShort565|UnormShort555|UnormInt101010|SignedInt8|SignedInt16|SignedInt32|UnsignedInt8|UnsignedInt16|UnsignedInt32|HalfFloat|Float|UnormInt24|UnormInt101010_2)\\b",
"name": "keyword.spirv"
},
"ValueEnum_FPRoundingMode": {
"match": "\\b(RTE|RTZ|RTP|RTN)\\b",
"name": "keyword.spirv"
},
"ValueEnum_LinkageType": {
"match": "\\b(Export|Import)\\b",
"name": "keyword.spirv"
},
"ValueEnum_AccessQualifier": {
"match": "\\b(ReadOnly|WriteOnly|ReadWrite)\\b",
"name": "keyword.spirv"
},
"ValueEnum_FunctionParameterAttribute": {
"match": "\\b(Zext|Sext|ByVal|Sret|NoAlias|NoCapture|NoWrite|NoReadWrite)\\b",
"name": "keyword.spirv"
},
"ValueEnum_Decoration": {
"match": "\\b(RelaxedPrecision|SpecId|Block|BufferBlock|RowMajor|ColMajor|ArrayStride|MatrixStride|GLSLShared|GLSLPacked|CPacked|BuiltIn|NoPerspective|Flat|Patch|Centroid|Sample|Invariant|Restrict|Aliased|Volatile|Constant|Coherent|NonWritable|NonReadable|Uniform|UniformId|SaturatedConversion|Stream|Location|Component|Index|Binding|DescriptorSet|Offset|XfbBuffer|XfbStride|FuncParamAttr|FPRoundingMode|FPFastMathMode|LinkageAttributes|NoContraction|InputAttachmentIndex|Alignment|MaxByteOffset|AlignmentId|MaxByteOffsetId|NoSignedWrap|NoUnsignedWrap|ExplicitInterpAMD|OverrideCoverageNV|PassthroughNV|ViewportRelativeNV|SecondaryViewportRelativeNV|PerPrimitiveNV|PerViewNV|PerTaskNV|PerVertexNV|NonUniform|NonUniformEXT|RestrictPointer|RestrictPointerEXT|AliasedPointer|AliasedPointerEXT|CounterBuffer|HlslCounterBufferGOOGLE|UserSemantic|HlslSemanticGOOGLE|UserTypeGOOGLE)\\b",
"name": "keyword.spirv"
},
"ValueEnum_BuiltIn": {
"match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|SubgroupEqMaskKHR|SubgroupGeMaskKHR|SubgroupGtMaskKHR|SubgroupLeMaskKHR|SubgroupLtMaskKHR|BaseVertex|BaseInstance|DrawIndex|DeviceIndex|ViewIndex|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordNV|BaryCoordNoPerspNV|FragSizeEXT|FragmentSizeNV|FragInvocationCountEXT|InvocationsPerPixelNV|LaunchIdNV|LaunchSizeNV|WorldRayOriginNV|WorldRayDirectionNV|ObjectRayOriginNV|ObjectRayDirectionNV|RayTminNV|RayTmaxNV|InstanceCustomIndexNV|ObjectToWorldNV|WorldToObjectNV|HitTNV|HitKindNV|IncomingRayFlagsNV|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV)\\b",
"name": "keyword.spirv"
},
"ValueEnum_Scope": {
"match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|QueueFamilyKHR)\\b",
"name": "keyword.spirv"
},
"ValueEnum_GroupOperation": {
"match": "\\b(Reduce|InclusiveScan|ExclusiveScan|ClusteredReduce|PartitionedReduceNV|PartitionedInclusiveScanNV|PartitionedExclusiveScanNV)\\b",
"name": "keyword.spirv"
},
"ValueEnum_KernelEnqueueFlags": {
"match": "\\b(NoWait|WaitKernel|WaitWorkGroup)\\b",
"name": "keyword.spirv"
},
"ValueEnum_Capability": {
"match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b",
"name": "keyword.spirv"
},
"opcode": {
"match": "(Op[a-zA-Z]+)",
"name": "entity.name.function.spirv"
},
"identifier": {
"match": "%[a-zA-Z0-9_]+",
"name": "variable.spirv"
},
"number": {
"match": "\\b[0-9]+.?[0-9]*\\b",
"name": "constant.numeric.spirv"
},
"comment": {
"match": ";[^\n]*",
"name": "comment.line.spirv"
},
"operator": {
"match": "=",
"name": "keyword.operator.spirv"
},
"string": {
"begin": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.spirv"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.spirv"
}
},
"name": "string.quoted.double.spirv",
"patterns": [ { "include": "#string_escaped_char" } ]
},
"string_escaped_char": {
"patterns": [
{
"match": "\\\\([0-7]{3}|[abfnrtv\\\\'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})",
"name": "constant.character.escape.spirv"
}, {
"match": "\\\\[^0-7xuUabfnrtv\\'\"]",
"name": "invalid.illegal.unknown-escape.spirv"
}
]
}
}
}

View File

@@ -0,0 +1,67 @@
{
"scopeName": "source.spirv",
"name": "SPIR-V",
"comment": "Generated by {{GenerateArguments}}. Do not modify this file directly.",
"patterns": [
{{range $o := .OperandKinds}}{{if len $o.Enumerants}} { "include": "#{{$o.Category}}_{{$o.Kind}}" },
{{end}}{{end}} { "include": "#opcode" },
{ "include": "#identifier" },
{ "include": "#number" },
{ "include": "#string" },
{ "include": "#comment" },
{ "include": "#operator" }
],
"repository": { {{range $o := .OperandKinds}}{{if len $o.Enumerants}}
"{{$o.Category}}_{{$o.Kind}}": {
"match": "\\b({{OperandKindsMatch $o}})\\b",
"name": "keyword.spirv"
},{{end}}{{end}}
"opcode": {
"match": "(Op[a-zA-Z]+)",
"name": "entity.name.function.spirv"
},
"identifier": {
"match": "%[a-zA-Z0-9_]+",
"name": "variable.spirv"
},
"number": {
"match": "\\b[0-9]+.?[0-9]*\\b",
"name": "constant.numeric.spirv"
},
"comment": {
"match": ";[^\n]*",
"name": "comment.line.spirv"
},
"operator": {
"match": "=",
"name": "keyword.operator.spirv"
},
"string": {
"begin": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.spirv"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.spirv"
}
},
"name": "string.quoted.double.spirv",
"patterns": [ { "include": "#string_escaped_char" } ]
},
"string_escaped_char": {
"patterns": [
{
"match": "\\\\([0-7]{3}|[abfnrtv\\\\'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})",
"name": "constant.character.escape.spirv"
}, {
"match": "\\\\[^0-7xuUabfnrtv\\'\"]",
"name": "invalid.illegal.unknown-escape.spirv"
}
]
}
}
}

View File

@@ -0,0 +1,75 @@
// Copyright (C) 2019 Google Inc.
//
// 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.
// Package grammar holds the JSON type definitions for the SPIR-V grammar schema.
//
// See https://www.khronos.org/registry/spir-v/specs/unified1/MachineReadableGrammar.html
// for more information.
package grammar
// Root is the top-level structure of the JSON grammar.
type Root struct {
MagicNumber string `json:"magic_number"`
MajorVersion int `json:"major_version"`
MinorVersion int `json:"minor_version"`
Revision int `json:"revision"`
Instructions []Instruction `json:"instructions"`
OperandKinds []OperandKind `json:"operand_kinds"`
}
// Instruction holds information about a specific SPIR-V instruction.
type Instruction struct {
Opname string `json:"opname"`
Class string `json:"class"`
Opcode int `json:"opcode"`
Operands []Operand `json:"operands"`
}
// Operand contains information about a logical operand for an instruction.
type Operand struct {
Kind string `json:"kind"`
Name string `json:"name"`
Quantifier Quantifier `json:"quantifier"`
}
// OperandKind contains information about a specific operand kind.
type OperandKind struct {
Category string `json:"category"`
Kind string `json:"kind"`
Enumerants []Enumerant `json:"enumerants"`
Bases []string `json:"bases"`
}
// Enumerant contains information about an enumerant in an enum.
type Enumerant struct {
Enumerant string `json:"enumerant"`
Value interface{} `json:"value"`
Capabilities []string `json:"capabilities"`
Parameters []Parameter `json:"parameters"`
Version string `json:"version"`
}
// Parameter contains information about a logical parameter for an enumerant.
type Parameter struct {
Kind string `json:"kind"`
Name string `json:"name"`
}
type Quantifier string
const (
Once Quantifier = ""
ZeroOrOnce Quantifier = "?"
ZeroOrMany Quantifier = "*"
)

View File

@@ -0,0 +1,167 @@
// Copyright (C) 2019 Google Inc.
//
// 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.
// gen-grammar generates the spirv.json grammar file from the official SPIR-V
// grammar JSON file.
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"text/template"
"github.com/pkg/errors"
"../grammar"
)
const (
spirvGrammarURL = "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json"
spirvGrammarName = "spirv.core.grammar.json"
)
var (
templatePath = flag.String("template", "", "Path to input template file (required)")
outputPath = flag.String("out", "", "Path to output generated file (required)")
cachePath = flag.String("cache", "", "Cache directory for downloaded files (optional)")
thisDir = func() string {
_, file, _, _ := runtime.Caller(1)
return filepath.Dir(file)
}()
)
func main() {
flag.Parse()
if *templatePath == "" || *outputPath == "" {
flag.Usage()
os.Exit(1)
}
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run() error {
tf, err := ioutil.ReadFile(*templatePath)
if err != nil {
return errors.Wrap(err, "Could not open template file")
}
t, err := template.New("tmpl").
Funcs(template.FuncMap{
"GenerateArguments": func() string {
relPath := func(path string) string {
rel, err := filepath.Rel(thisDir, path)
if err != nil {
return path
}
return rel
}
args := []string{
"--template=" + relPath(*templatePath),
"--out=" + relPath(*outputPath),
}
return "gen-grammar.go " + strings.Join(args, " ")
},
"OperandKindsMatch": func(k grammar.OperandKind) string {
sb := strings.Builder{}
for i, e := range k.Enumerants {
if i > 0 {
sb.WriteString("|")
}
sb.WriteString(e.Enumerant)
}
return sb.String()
},
}).Parse(string(tf))
if err != nil {
return errors.Wrap(err, "Failed to parse template")
}
file, err := getOrDownload(spirvGrammarName, spirvGrammarURL)
if err != nil {
return errors.Wrap(err, "Failed to load grammar file")
}
g := grammar.Root{}
if err := json.NewDecoder(bytes.NewReader(file)).Decode(&g); err != nil {
return errors.Wrap(err, "Failed to parse grammar file")
}
buf := bytes.Buffer{}
if err := t.Execute(&buf, g); err != nil {
return errors.Wrap(err, "Failed to execute template")
}
out := buf.String()
out = strings.ReplaceAll(out, "•", "")
if err := ioutil.WriteFile(*outputPath, []byte(out), 0777); err != nil {
return errors.Wrap(err, "Failed to write output file")
}
return nil
}
// getOrDownload loads the specific file from the cache, or downloads the file
// from the given url.
func getOrDownload(name, url string) ([]byte, error) {
if *cachePath != "" {
if err := os.MkdirAll(*cachePath, 0777); err == nil {
path := filepath.Join(*cachePath, name)
if isFile(path) {
return ioutil.ReadFile(path)
}
}
}
resp, err := http.Get(url)
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if *cachePath != "" {
ioutil.WriteFile(filepath.Join(*cachePath, name), data, 0777)
}
return data, nil
}
// isFile returns true if path is a file.
func isFile(path string) bool {
s, err := os.Stat(path)
if err != nil {
return false
}
return !s.IsDir()
}
// isDir returns true if path is a directory.
func isDir(path string) bool {
s, err := os.Stat(path)
if err != nil {
return false
}
return s.IsDir()
}