From 895dafcc1d1a2418f0d12e0986ce31e376b675c5 Mon Sep 17 00:00:00 2001 From: Vasyl Teliman Date: Fri, 23 Oct 2020 16:49:50 +0300 Subject: [PATCH] spirv-fuzz: Add FuzzerPassAddCompositeExtract (#3904) Fixes #3806. --- source/fuzz/CMakeLists.txt | 2 + .../data_synonym_and_id_equation_facts.cpp | 11 ++ .../data_synonym_and_id_equation_facts.h | 3 + source/fuzz/fact_manager/fact_manager.cpp | 5 + source/fuzz/fact_manager/fact_manager.h | 4 + source/fuzz/fuzzer.cpp | 2 + source/fuzz/fuzzer_context.cpp | 7 + source/fuzz/fuzzer_context.h | 12 ++ .../fuzzer_pass_add_composite_extract.cpp | 162 ++++++++++++++++++ .../fuzz/fuzzer_pass_add_composite_extract.h | 40 +++++ .../pass_management/repeated_pass_instances.h | 2 + .../repeated_pass_recommender_standard.cpp | 4 + .../fuzz/transformation_composite_extract.cpp | 34 ++-- .../transformation_composite_extract_test.cpp | 7 +- 14 files changed, 270 insertions(+), 25 deletions(-) create mode 100644 source/fuzz/fuzzer_pass_add_composite_extract.cpp create mode 100644 source/fuzz/fuzzer_pass_add_composite_extract.h diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index c936ca824..a0e7ed8f3 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -54,6 +54,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass.h fuzzer_pass_add_access_chains.h fuzzer_pass_add_bit_instruction_synonyms.h + fuzzer_pass_add_composite_extract.h fuzzer_pass_add_composite_inserts.h fuzzer_pass_add_composite_types.h fuzzer_pass_add_copy_memory.h @@ -241,6 +242,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass.cpp fuzzer_pass_add_access_chains.cpp fuzzer_pass_add_bit_instruction_synonyms.cpp + fuzzer_pass_add_composite_extract.cpp fuzzer_pass_add_composite_inserts.cpp fuzzer_pass_add_composite_types.cpp fuzzer_pass_add_copy_memory.cpp diff --git a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp index 0308d50ed..ad4cd0c07 100644 --- a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp +++ b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp @@ -909,6 +909,17 @@ DataSynonymAndIdEquationFacts::GetIdsForWhichSynonymsAreKnown() const { return result; } +std::vector +DataSynonymAndIdEquationFacts::GetAllKnownSynonyms() const { + std::vector result; + for (const auto* dd : synonymous_.GetAllKnownValues()) { + if (ObjectStillExists(*dd)) { + result.push_back(dd); + } + } + return result; +} + bool DataSynonymAndIdEquationFacts::IsSynonymous( const protobufs::DataDescriptor& data_descriptor1, const protobufs::DataDescriptor& data_descriptor2) const { diff --git a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h index f8a01237b..6652f30aa 100644 --- a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h +++ b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h @@ -65,6 +65,9 @@ class DataSynonymAndIdEquationFacts { // See method in FactManager which delegates to this method. std::vector GetIdsForWhichSynonymsAreKnown() const; + // See method in FactManager which delegates to this method. + std::vector GetAllKnownSynonyms() const; + // See method in FactManager which delegates to this method. bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1, const protobufs::DataDescriptor& data_descriptor2) const; diff --git a/source/fuzz/fact_manager/fact_manager.cpp b/source/fuzz/fact_manager/fact_manager.cpp index 29050e9ef..40c0865f4 100644 --- a/source/fuzz/fact_manager/fact_manager.cpp +++ b/source/fuzz/fact_manager/fact_manager.cpp @@ -177,6 +177,11 @@ std::vector FactManager::GetIdsForWhichSynonymsAreKnown() const { return data_synonym_and_id_equation_facts_.GetIdsForWhichSynonymsAreKnown(); } +std::vector FactManager::GetAllSynonyms() + const { + return data_synonym_and_id_equation_facts_.GetAllKnownSynonyms(); +} + std::vector FactManager::GetSynonymsForDataDescriptor( const protobufs::DataDescriptor& data_descriptor) const { diff --git a/source/fuzz/fact_manager/fact_manager.h b/source/fuzz/fact_manager/fact_manager.h index 78769c10a..5cf5b18bc 100644 --- a/source/fuzz/fact_manager/fact_manager.h +++ b/source/fuzz/fact_manager/fact_manager.h @@ -149,6 +149,10 @@ class FactManager { // this piece of data" is known. std::vector GetIdsForWhichSynonymsAreKnown() const; + // Returns a vector of all data descriptors that participate in DataSynonym + // facts. All descriptors are guaranteed to exist in the |ir_context_|. + std::vector GetAllSynonyms() const; + // Returns the equivalence class of all known synonyms of |id|, or an empty // set if no synonyms are known. std::vector GetSynonymsForId( diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 64b0772aa..f326656b7 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -23,6 +23,7 @@ #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/fuzzer_pass_add_access_chains.h" #include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_composite_extract.h" #include "source/fuzz/fuzzer_pass_add_composite_inserts.h" #include "source/fuzz/fuzzer_pass_add_composite_types.h" #include "source/fuzz/fuzzer_pass_add_copy_memory.h" @@ -228,6 +229,7 @@ Fuzzer::FuzzerResult Fuzzer::Run() { // if it is enabled. MaybeAddRepeatedPass(&pass_instances); MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); MaybeAddRepeatedPass(&pass_instances); MaybeAddRepeatedPass(&pass_instances); MaybeAddRepeatedPass(&pass_instances); diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index 19d4ca55e..3a27d6f53 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -35,6 +35,7 @@ const std::pair kChanceOfAddingBitInstructionSynonym = {5, 20}; const std::pair kChanceOfAddingBothBranchesWhenReplacingOpSelect = {40, 60}; +const std::pair kChanceOfAddingCompositeExtract = {20, 50}; const std::pair kChanceOfAddingCompositeInsert = {20, 50}; const std::pair kChanceOfAddingCopyMemory = {20, 50}; const std::pair kChanceOfAddingDeadBlock = {20, 90}; @@ -82,6 +83,8 @@ const std::pair kChanceOfDuplicatingRegionWithSelection = { 20, 50}; const std::pair kChanceOfFlatteningConditionalBranch = {45, 95}; +const std::pair kChanceOfGoingDeeperToExtractComposite = { + 30, 70}; const std::pair kChanceOfGoingDeeperToInsertInComposite = { 30, 70}; const std::pair kChanceOfGoingDeeperWhenMakingAccessChain = @@ -197,6 +200,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfAddingBitInstructionSynonym); chance_of_adding_both_branches_when_replacing_opselect_ = ChooseBetweenMinAndMax(kChanceOfAddingBothBranchesWhenReplacingOpSelect); + chance_of_adding_composite_extract_ = + ChooseBetweenMinAndMax(kChanceOfAddingCompositeExtract); chance_of_adding_composite_insert_ = ChooseBetweenMinAndMax(kChanceOfAddingCompositeInsert); chance_of_adding_copy_memory_ = @@ -263,6 +268,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfDuplicatingRegionWithSelection); chance_of_flattening_conditional_branch_ = ChooseBetweenMinAndMax(kChanceOfFlatteningConditionalBranch); + chance_of_going_deeper_to_extract_composite_ = + ChooseBetweenMinAndMax(kChanceOfGoingDeeperToExtractComposite); chance_of_going_deeper_to_insert_in_composite_ = ChooseBetweenMinAndMax(kChanceOfGoingDeeperToInsertInComposite); chance_of_going_deeper_when_making_access_chain_ = diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index 147c66dd1..b230af32b 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -127,6 +127,9 @@ class FuzzerContext { uint32_t GetChanceOfAddingBothBranchesWhenReplacingOpSelect() { return chance_of_adding_both_branches_when_replacing_opselect_; } + uint32_t GetChanceOfAddingCompositeExtract() { + return chance_of_adding_composite_extract_; + } uint32_t GetChanceOfAddingCompositeInsert() { return chance_of_adding_composite_insert_; } @@ -216,6 +219,9 @@ class FuzzerContext { uint32_t GetChanceOfFlatteningConditionalBranch() { return chance_of_flattening_conditional_branch_; } + uint32_t GetChanceOfGoingDeeperToExtractComposite() { + return chance_of_going_deeper_to_extract_composite_; + } uint32_t GetChanceOfGoingDeeperToInsertInComposite() { return chance_of_going_deeper_to_insert_in_composite_; } @@ -354,6 +360,10 @@ class FuzzerContext { return components; } + uint32_t GetRandomCompositeExtractIndex(uint32_t number_of_members) { + assert(number_of_members > 0 && "Composite object must have some members"); + return ChooseBetweenMinAndMax({0, number_of_members - 1}); + } uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) { return random_generator_->RandomUint32(composite_size_bound); } @@ -416,6 +426,7 @@ class FuzzerContext { uint32_t chance_of_adding_array_or_struct_type_; uint32_t chance_of_adding_bit_instruction_synonym_; uint32_t chance_of_adding_both_branches_when_replacing_opselect_; + uint32_t chance_of_adding_composite_extract_; uint32_t chance_of_adding_composite_insert_; uint32_t chance_of_adding_copy_memory_; uint32_t chance_of_adding_dead_block_; @@ -451,6 +462,7 @@ class FuzzerContext { uint32_t chance_of_donating_additional_module_; uint32_t chance_of_duplicating_region_with_selection_; uint32_t chance_of_flattening_conditional_branch_; + uint32_t chance_of_going_deeper_to_extract_composite_; uint32_t chance_of_going_deeper_to_insert_in_composite_; uint32_t chance_of_going_deeper_when_making_access_chain_; uint32_t chance_of_having_two_blocks_in_loop_to_create_int_synonym_; diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/source/fuzz/fuzzer_pass_add_composite_extract.cpp new file mode 100644 index 000000000..3b40d5fa0 --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_composite_extract.cpp @@ -0,0 +1,162 @@ +// 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 "source/fuzz/fuzzer_pass_add_composite_extract.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_composite_extract.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCompositeExtract::FuzzerPassAddCompositeExtract( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddCompositeExtract::~FuzzerPassAddCompositeExtract() = default; + +void FuzzerPassAddCompositeExtract::Apply() { + std::vector composite_synonyms; + for (const auto* dd : + GetTransformationContext()->GetFactManager()->GetAllSynonyms()) { + // |dd| must describe a component of a composite. + if (!dd->index().empty()) { + composite_synonyms.push_back(dd); + } + } + + // We don't want to invalidate the module every time we apply this + // transformation since rebuilding DominatorAnalysis can be expensive, so we + // collect up the transformations we wish to apply and apply them all later. + std::vector transformations; + + ForEachInstructionWithInstructionDescriptor( + [this, &composite_synonyms, &transformations]( + opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract, + inst_it)) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingCompositeExtract())) { + return; + } + + auto available_composites = FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext* ir_context, opt::Instruction* inst) { + return inst->type_id() && inst->result_id() && + fuzzerutil::IsCompositeType( + ir_context->get_type_mgr()->GetType(inst->type_id())); + }); + + std::vector available_synonyms; + for (const auto* dd : composite_synonyms) { + if (fuzzerutil::IdIsAvailableBeforeInstruction( + GetIRContext(), &*inst_it, dd->object())) { + available_synonyms.push_back(dd); + } + } + + if (available_synonyms.empty() && available_composites.empty()) { + return; + } + + uint32_t composite_id = 0; + std::vector indices; + + if (available_synonyms.empty() || (!available_composites.empty() && + GetFuzzerContext()->ChooseEven())) { + const auto* inst = + available_composites[GetFuzzerContext()->RandomIndex( + available_composites)]; + composite_id = inst->result_id(); + + const auto* type = + GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Composite instruction has invalid type id"); + + do { + uint32_t number_of_members = 0; + + if (const auto* array_type = type->AsArray()) { + const auto* type_inst = + GetIRContext()->get_def_use_mgr()->GetDef(inst->type_id()); + assert(type_inst && "Type instruction must exist"); + + number_of_members = + fuzzerutil::GetArraySize(*type_inst, GetIRContext()); + type = array_type->element_type(); + } else if (const auto* vector_type = type->AsVector()) { + number_of_members = vector_type->element_count(); + type = vector_type->element_type(); + } else if (const auto* matrix_type = type->AsMatrix()) { + number_of_members = matrix_type->element_count(); + type = matrix_type->element_type(); + } else if (const auto* struct_type = type->AsStruct()) { + number_of_members = + static_cast(struct_type->element_types().size()); + // The next value of |type| will be assigned when we know the + // index of the OpTypeStruct's operand. + } else { + assert(false && "|inst| is not a composite"); + return; + } + + if (number_of_members == 0) { + return; + } + + indices.push_back( + GetFuzzerContext()->GetRandomCompositeExtractIndex( + number_of_members)); + + if (const auto* struct_type = type->AsStruct()) { + type = struct_type->element_types()[indices.back()]; + } + } while (fuzzerutil::IsCompositeType(type) && + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfGoingDeeperToExtractComposite())); + } else { + const auto* dd = available_synonyms[GetFuzzerContext()->RandomIndex( + available_synonyms)]; + + composite_id = dd->object(); + indices.assign(dd->index().begin(), dd->index().end()); + } + + assert(composite_id != 0 && !indices.empty() && + "Composite object should have been chosen correctly"); + + transformations.emplace_back(instruction_descriptor, + GetFuzzerContext()->GetFreshId(), + composite_id, indices); + }); + + for (const auto& transformation : transformations) { + ApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.h b/source/fuzz/fuzzer_pass_add_composite_extract.h new file mode 100644 index 000000000..8bcb825fb --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_composite_extract.h @@ -0,0 +1,40 @@ +// 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_FUZZER_PASS_ADD_COMPOSITE_EXTRACT_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_EXTRACT_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly decides whether to add OpCompositeExtract before some instruction +// in the module. +class FuzzerPassAddCompositeExtract : public FuzzerPass { + public: + FuzzerPassAddCompositeExtract( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddCompositeExtract() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_EXTRACT_H_ diff --git a/source/fuzz/pass_management/repeated_pass_instances.h b/source/fuzz/pass_management/repeated_pass_instances.h index 6809d0786..5f479fbe6 100644 --- a/source/fuzz/pass_management/repeated_pass_instances.h +++ b/source/fuzz/pass_management/repeated_pass_instances.h @@ -17,6 +17,7 @@ #include "source/fuzz/fuzzer_pass_add_access_chains.h" #include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_composite_extract.h" #include "source/fuzz/fuzzer_pass_add_composite_inserts.h" #include "source/fuzz/fuzzer_pass_add_composite_types.h" #include "source/fuzz/fuzzer_pass_add_copy_memory.h" @@ -110,6 +111,7 @@ class RepeatedPassInstances { REPEATED_PASS_INSTANCE(AddAccessChains); REPEATED_PASS_INSTANCE(AddBitInstructionSynonyms); + REPEATED_PASS_INSTANCE(AddCompositeExtract); REPEATED_PASS_INSTANCE(AddCompositeInserts); REPEATED_PASS_INSTANCE(AddCompositeTypes); REPEATED_PASS_INSTANCE(AddCopyMemory); diff --git a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp index 060c7438d..7121c330c 100644 --- a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp +++ b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp @@ -40,6 +40,10 @@ RepeatedPassRecommenderStandard::GetFuturePassRecommendations( // - Adding bit instruction synonyms creates opportunities to apply synonyms return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); } + if (&pass == pass_instances_->GetAddCompositeExtract()) { + // - This transformation can introduce synonyms to the fact manager. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } if (&pass == pass_instances_->GetAddCompositeInserts()) { // - Having added inserts we will have more vectors, so there is scope for // vector shuffling diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp index 291331ca6..2aff02fb3 100644 --- a/source/fuzz/transformation_composite_extract.cpp +++ b/source/fuzz/transformation_composite_extract.cpp @@ -55,19 +55,14 @@ bool TransformationCompositeExtract::IsApplicable( if (!composite_instruction) { return false; } - if (auto block = ir_context->get_instr_block(composite_instruction)) { - if (composite_instruction == instruction_to_insert_before || - !ir_context->GetDominatorAnalysis(block->GetParent()) - ->Dominates(composite_instruction, instruction_to_insert_before)) { - return false; - } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.composite_id())) { + return false; } - assert(composite_instruction->type_id() && - "An instruction in a block cannot have a result id but no type id."); auto composite_type = ir_context->get_type_mgr()->GetType(composite_instruction->type_id()); - if (!composite_type) { + if (!fuzzerutil::IsCompositeType(composite_type)) { return false; } @@ -133,19 +128,14 @@ void TransformationCompositeExtract::AddDataSynonymFacts( // Add the fact that the id storing the extracted element is synonymous with // the index into the structure. - if (!transformation_context->GetFactManager()->IdIsIrrelevant( - message_.composite_id())) { - std::vector indices; - for (auto an_index : message_.index()) { - indices.push_back(an_index); - } - protobufs::DataDescriptor data_descriptor_for_extracted_element = - MakeDataDescriptor(message_.composite_id(), indices); - protobufs::DataDescriptor data_descriptor_for_result_id = - MakeDataDescriptor(message_.fresh_id(), {}); - transformation_context->GetFactManager()->AddFactDataSynonym( - data_descriptor_for_extracted_element, data_descriptor_for_result_id); - } + std::vector indices(message_.index().begin(), + message_.index().end()); + auto data_descriptor_for_extracted_element = + MakeDataDescriptor(message_.composite_id(), indices); + auto data_descriptor_for_result_id = + MakeDataDescriptor(message_.fresh_id(), {}); + transformation_context->GetFactManager()->AddFactDataSynonym( + data_descriptor_for_extracted_element, data_descriptor_for_result_id); } } // namespace fuzz diff --git a/test/fuzz/transformation_composite_extract_test.cpp b/test/fuzz/transformation_composite_extract_test.cpp index 1ee62f59a..383a4db1a 100644 --- a/test/fuzz/transformation_composite_extract_test.cpp +++ b/test/fuzz/transformation_composite_extract_test.cpp @@ -107,9 +107,10 @@ TEST(TransformationCompositeExtractTest, BasicTest) { .IsApplicable(context.get(), transformation_context)); // Id for composite is not a composite. - ASSERT_FALSE(TransformationCompositeExtract( - MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 27, {}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationCompositeExtract( + MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 32, {}) + .IsApplicable(context.get(), transformation_context)); // Composite does not dominate instruction being inserted before. ASSERT_FALSE(