{"id":2229947,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2229947/?format=json","web_url":"http://patchwork.ozlabs.org/project/gcc/patch/20260428232805.1953949-1-dmalcolm@redhat.com/","project":{"id":17,"url":"http://patchwork.ozlabs.org/api/1.1/projects/17/?format=json","name":"GNU Compiler Collection","link_name":"gcc","list_id":"gcc-patches.gcc.gnu.org","list_email":"gcc-patches@gcc.gnu.org","web_url":null,"scm_url":null,"webscm_url":null},"msgid":"<20260428232805.1953949-1-dmalcolm@redhat.com>","date":"2026-04-28T23:28:05","name":"[pushed:,r17-182] analyzer: use concrete_binding_map for compound_svalue (PR analyzer/123145)","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"931482d1f46077c8224d30c2c17810bdcc0a8352","submitter":{"id":24465,"url":"http://patchwork.ozlabs.org/api/1.1/people/24465/?format=json","name":"David Malcolm","email":"dmalcolm@redhat.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/gcc/patch/20260428232805.1953949-1-dmalcolm@redhat.com/mbox/","series":[{"id":501956,"url":"http://patchwork.ozlabs.org/api/1.1/series/501956/?format=json","web_url":"http://patchwork.ozlabs.org/project/gcc/list/?series=501956","date":"2026-04-28T23:28:05","name":"[pushed:,r17-182] analyzer: use concrete_binding_map for compound_svalue (PR analyzer/123145)","version":1,"mbox":"http://patchwork.ozlabs.org/series/501956/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2229947/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2229947/checks/","tags":{},"headers":{"Return-Path":"<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>","X-Original-To":["incoming@patchwork.ozlabs.org","gcc-patches@gcc.gnu.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","gcc-patches@gcc.gnu.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=PX00ErC0;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)","sourceware.org;\n\tdkim=pass (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=PX00ErC0","sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com","sourceware.org; spf=pass smtp.mailfrom=redhat.com","server2.sourceware.org;\n arc=none smtp.remote-ip=170.10.129.124"],"Received":["from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g4xVb0Mppz1yHv\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 29 Apr 2026 09:29:59 +1000 (AEST)","from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 358824BBC0EA\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 28 Apr 2026 23:29:57 +0000 (GMT)","from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n by sourceware.org (Postfix) with ESMTP id D9DB34BBC0E2\n for <gcc-patches@gcc.gnu.org>; Tue, 28 Apr 2026 23:28:12 +0000 (GMT)","from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-571-MW-D5uDSM8u83vgYvoCR2Q-1; Tue,\n 28 Apr 2026 19:28:09 -0400","from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id F25A21956052\n for <gcc-patches@gcc.gnu.org>; Tue, 28 Apr 2026 23:28:08 +0000 (UTC)","from t14s.localdomain.com (unknown [10.22.65.168])\n by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id 3467B195608E; Tue, 28 Apr 2026 23:28:07 +0000 (UTC)"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 sourceware.org 358824BBC0EA","OpenDKIM Filter v2.11.0 sourceware.org D9DB34BBC0E2"],"DMARC-Filter":"OpenDMARC Filter v1.4.2 sourceware.org D9DB34BBC0E2","ARC-Filter":"OpenARC Filter v1.0.0 sourceware.org D9DB34BBC0E2","ARC-Seal":"i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777418893; cv=none;\n b=JvsiaeD/btzWA4QiVtCADXZZ0dao65miV5FHZXTwPBLb71KJW7XJiF3+rakSvI+brg3Je4U09gjzDiTxuPuQHPpm3E5wHS5V1ZP4xeBZ/656DLWt0hXVvZa3rlaz4QCov59/feWCAGedzy1Tzm216sDQr2h0eFqg+/TMuzzMg84=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1777418893; c=relaxed/simple;\n bh=Ql3RT8Z2YQosyT35duhQ5nIG6tdYBnog34KQMDWAoes=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=k5X861s8rbh5BckaR6xRBKbOeGEpXP7JJ97Ng0jSJObDOyXCNa/fjpjBDc9B/GQ76MAM55U8g4fIwvreJcBFuA+z+vBtCLpC4hDhij/7dcunhKxdCf8ubRyG952QT4TZE1BXNZOkg/7VrvLrxBJG3uFevW/1n9u5tYem1jV4m3k=","ARC-Authentication-Results":"i=1; server2.sourceware.org","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1777418892;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding;\n bh=6kXxJOwbdYIckRiRrWuzLxrfiYtWv7plabCXuZcFyKQ=;\n b=PX00ErC0UBu+SmMadlyvciarWmGiRSVeAkMDzX5ZCGI2V7yBfWgYxO1kqXgntcDymQ8D5j\n p4GSVRv2Ifev74HaNAoO3/eGTMC5tYAbFVX2APttk8sSQsWX91gW6Su+imKXzy2PKuzhu7\n oaQ51VfCIvyOQbxkHPjBCCmngEsX5jM=","X-MC-Unique":"MW-D5uDSM8u83vgYvoCR2Q-1","X-Mimecast-MFC-AGG-ID":"MW-D5uDSM8u83vgYvoCR2Q_1777418889","From":"David Malcolm <dmalcolm@redhat.com>","To":"gcc-patches@gcc.gnu.org","Cc":"David Malcolm <dmalcolm@redhat.com>","Subject":"[pushed: r17-182] analyzer: use concrete_binding_map for\n compound_svalue (PR analyzer/123145)","Date":"Tue, 28 Apr 2026 19:28:05 -0400","Message-ID":"<20260428232805.1953949-1-dmalcolm@redhat.com>","MIME-Version":"1.0","X-Scanned-By":"MIMEDefang 3.0 on 10.30.177.17","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"j_GueS2nv0CPzQJHcSEe95Fg-zj38c3Mz2UMhgQlXQM_1777418889","X-Mimecast-Originator":"redhat.com","Content-Transfer-Encoding":"8bit","content-type":"text/plain; charset=\"US-ASCII\"; x-default=true","X-BeenThere":"gcc-patches@gcc.gnu.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"Gcc-patches mailing list <gcc-patches.gcc.gnu.org>","List-Unsubscribe":"<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>","List-Archive":"<https://gcc.gnu.org/pipermail/gcc-patches/>","List-Post":"<mailto:gcc-patches@gcc.gnu.org>","List-Help":"<mailto:gcc-patches-request@gcc.gnu.org?subject=help>","List-Subscribe":"<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>","Errors-To":"gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org"},"content":"A compound_svalue can only have concrete bindings.  Capture this in the\ntype system by splitting out the concrete parts of class binding_map\ninto a new class concrete_binding_map, and use the latter for\ncompound_svalue.  This also allows some simplifications and\noptimizations, where we can use bit_range rather than binding keys.\n\nNo functional change intended.\n\nSuccessfully bootstrapped & regrtested on powerpc64le-unknown-linux-gnu\n(albeit without ada,cobol,d, and possibly missing some optional test dependencies)\nPushed to trunk as r17-182-g3a8301ae70cb7a.\n\ngcc/analyzer/ChangeLog:\n\tPR analyzer/123145\n\t* access-diagram.cc\n\t(compound_svalue_spatial_item::compound_svalue_spatial_item):\n\tUpdate for compound_svalue using concrete_binding_map rather than\n\tbinding_map.\n\t* bounds-checking.cc (strip_types): Likewise.\n\t* call-summary.cc\n\t(call_summary_replay::convert_svalue_from_summary_1): Update for\n\treimplementation of class binding_map.\n\t(call_summary_replay::convert_svalue_from_summary_1): Likewise.\n\t* infinite-recursion.cc (contains_unknown_p): Update for\n\tcompound_svalue using concrete_binding_map rather than\n\tbinding_map.\n\t* program-state.cc (sm_state_map::impl_set_state): Likewise.\n\t* region-model-manager.cc (maybe_undo_optimize_bit_field_compare):\n\tLikewise.\n\t(maybe_undo_optimize_bit_field_compare): Avoid building a\n\tconcrete_binding key by using get_any_exact_binding.\n\t(region_model_manager::get_or_create_compound_svalue): New\n\toverload, consuming a concrete_binding_map &&.\n\t* region-model-manager.h\n\t(region_model_manager::get_or_create_compound_svalue): New decl\n\tfor the above.\n\t* region-model-reachability.cc (reachable_regions::handle_sval):\n\tUpdate for compound_svalue using concrete_binding_map rather than\n\tbinding_map.\n\t(reachable_regions::handle_parm): Likewise.\n\t* region-model.cc (region_model::scan_for_null_terminator_1): Port\n\tfrom binding_map to concrete_binding_map.\n\t(exposure_through_uninit_copy::calc_num_uninit_bits): Update for\n\tcompound_svalue using concrete_binding_map rather than\n\tbinding_map.\n\t(contains_uninit_p): Likewise.\n\t* region.cc (decl_region::calc_svalue_for_constructor): Port from\n\tbinding_map to concrete_binding_map.\n\t(decl_region::get_svalue_for_initializer): Update call to\n\tget_or_create_compound_svalue.\n\t* store.cc (concrete_binding_map::dump_to_pp): New.\n\t(concrete_binding_map::dump): New.\n\t(concrete_binding_map::add_to_tree_widget): New.\n\t(concrete_binding_map::validate): New.\n\t(binding_map::cmp): Convert to...\n\t(concrete_binding_map::cmp): ...this.\n\t(concrete_binding_map::get_any_exact_binding): New.\n\t(concrete_binding_map::calc_complexity): New.\n\t(concrete_binding_map::remove_overlapping_binding): New.\n\t(concrete_binding_map::remove_overlapping_bindings): New.\n\t(concrete_binding_map::get_overlapping_bindings): New.\n\t(binding_map::put): Update for change to m_concrete.\n\t(binding_map::validate): Likewise.\n\t(binding_map::apply_ctor_to_region): Convert to...\n\t(concrete_binding_map::apply_ctor_to_region): ...this.\n\t(binding_map::apply_ctor_val_to_range): Convert to...\n\t(concrete_binding_map::apply_ctor_val_to_range): ...this.\n\t(binding_map::apply_ctor_pair_to_child_region): Convert to...\n\t(concrete_binding_map::apply_ctor_pair_to_child_region): ...this.\n\t(binding_map::remove_overlapping_bindings): Move part of\n\timplementation to\n\tconcrete_binding_map::remove_overlapping_binding.\n\t(binding_cluster::bind_compound_sval): Simplify using\n\tconcrete_binding_map.\n\t(binding_cluster::maybe_get_compound_binding): Likewise.\n\t(store::replay_call_summary_cluster): Update for change\n\tto compound_svalue.\n\t* store.h: Include \"analyzer/complexity.h\".\n\t(class concrete_binding_map): New, based on\n\tbinding_map::concrete_bindings_t.\n\t(binding_map::concrete_bindings_t): Use concrete_binding_map.\n\t(binding_map::empty_p): Update for above.\n\t(binding_map::apply_ctor_to_region): Drop decl.\n\t(binding_map::cmp): Likewise.\n\t(binding_map::apply_ctor_val_to_range): Likewise.\n\t(binding_map::apply_ctor_pair_to_child_region): Likewise.\n\t* svalue.cc (svalue::cmp_ptr): Update for change to\n\tcompound_svalue.\n\t(compound_svalue::compound_svalue): Port from binding_map to\n\tconcrete_binding_map.\n\t(compound_svalue::accept): Likewise.\n\t(compound_svalue::calc_complexity): Drop.\n\t(compound_svalue::maybe_fold_bits_within): Port from binding_map\n\tto concrete_binding_map.\n\t* svalue.h (class compound_svalue): Update leading comment.  Port\n\tfrom binding_map to concrete_binding_map.\n\nSigned-off-by: David Malcolm <dmalcolm@redhat.com>\n---\n gcc/analyzer/access-diagram.cc            |  23 +-\n gcc/analyzer/bounds-checking.cc           |  13 +-\n gcc/analyzer/call-summary.cc              |  42 +--\n gcc/analyzer/infinite-recursion.cc        |   2 +-\n gcc/analyzer/program-state.cc             |   2 +-\n gcc/analyzer/region-model-manager.cc      |  33 +-\n gcc/analyzer/region-model-manager.h       |   4 +-\n gcc/analyzer/region-model-reachability.cc |   4 +-\n gcc/analyzer/region-model.cc              |  15 +-\n gcc/analyzer/region.cc                    |   8 +-\n gcc/analyzer/store.cc                     | 419 ++++++++++++++--------\n gcc/analyzer/store.h                      | 131 ++++++-\n gcc/analyzer/svalue.cc                    | 150 ++++----\n gcc/analyzer/svalue.h                     |  28 +-\n 14 files changed, 538 insertions(+), 336 deletions(-)","diff":"diff --git a/gcc/analyzer/access-diagram.cc b/gcc/analyzer/access-diagram.cc\nindex 9f412b5ec76..6a3ad635f0c 100644\n--- a/gcc/analyzer/access-diagram.cc\n+++ b/gcc/analyzer/access-diagram.cc\n@@ -1212,23 +1212,18 @@ public:\n   : svalue_spatial_item (sval, bits, kind),\n     m_compound_sval (sval)\n   {\n-    const binding_map &map = m_compound_sval.get_map ();\n+    const concrete_binding_map &map = m_compound_sval.get_concrete_bindings ();\n     auto_vec <const binding_key *> binding_keys;\n     for (auto iter : map)\n       {\n-\tconst binding_key *key = iter.m_key;\n-\tconst svalue *bound_sval = iter.m_sval;\n-\tif (const concrete_binding *concrete_key\n-\t      = key->dyn_cast_concrete_binding ())\n-\t  {\n-\t    access_range range (nullptr,\n-\t\t\t\tconcrete_key->get_bit_range ());\n-\t    if (std::unique_ptr<spatial_item> child\n-\t\t  = make_existing_svalue_spatial_item (bound_sval,\n-\t\t\t\t\t\t       range,\n-\t\t\t\t\t\t       theme))\n-\t      m_children.push_back (std::move (child));\n-\t  }\n+\tconst bit_range &key = iter.first;\n+\tconst svalue *bound_sval = iter.second;\n+\taccess_range range (nullptr, key);\n+\tif (std::unique_ptr<spatial_item> child\n+\t    = make_existing_svalue_spatial_item (bound_sval,\n+\t\t\t\t\t\t range,\n+\t\t\t\t\t\t theme))\n+\t  m_children.push_back (std::move (child));\n       }\n   }\n \ndiff --git a/gcc/analyzer/bounds-checking.cc b/gcc/analyzer/bounds-checking.cc\nindex 8d78581a3df..f9b5165a7cb 100644\n--- a/gcc/analyzer/bounds-checking.cc\n+++ b/gcc/analyzer/bounds-checking.cc\n@@ -1334,14 +1334,15 @@ strip_types (const svalue *sval,\n     case SK_COMPOUND:\n       {\n \tconst compound_svalue *compound_sval = (const compound_svalue *)sval;\n-\tbinding_map typeless_map (*mgr.get_store_manager ());\n-\tfor (auto iter : compound_sval->get_map ())\n+\tconcrete_binding_map typeless_map;\n+\tfor (auto iter : compound_sval->get_concrete_bindings ())\n \t  {\n-\t    const binding_key *key = iter.m_key;\n-\t    const svalue *bound_sval = iter.m_sval;\n-\t    typeless_map.put (key, strip_types (bound_sval, mgr));\n+\t    const bit_range &bits = iter.first;\n+\t    const svalue *bound_sval = iter.second;\n+\t    typeless_map.insert (bits, strip_types (bound_sval, mgr));\n \t  }\n-\treturn mgr.get_or_create_compound_svalue (NULL_TREE, typeless_map);\n+\treturn mgr.get_or_create_compound_svalue (NULL_TREE,\n+\t\t\t\t\t\t  std::move (typeless_map));\n       }\n     case SK_CONJURED:\n       return sval;\ndiff --git a/gcc/analyzer/call-summary.cc b/gcc/analyzer/call-summary.cc\nindex 7a3d4aac7c6..9220958afca 100644\n--- a/gcc/analyzer/call-summary.cc\n+++ b/gcc/analyzer/call-summary.cc\n@@ -421,19 +421,12 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval)\n \tconst compound_svalue *compound_summary_sval\n \t  = as_a <const compound_svalue *> (summary_sval);\n \tregion_model_manager *mgr = get_manager ();\n-\tstore_manager *store_mgr = mgr->get_store_manager ();\n-\tbinding_map caller_map (*store_mgr);\n-\tauto_vec <const binding_key *> summary_keys;\n-\tfor (auto kv : *compound_summary_sval)\n-\t  summary_keys.safe_push (kv.m_key);\n-\tsummary_keys.qsort (binding_key::cmp_ptrs);\n-\tfor (auto key : summary_keys)\n+\tconcrete_binding_map caller_map;\n+\tfor (auto iter_summary : *compound_summary_sval)\n \t  {\n-\t    gcc_assert (key->concrete_p ());\n \t    /* No remapping is needed for concrete binding keys.  */\n-\n-\t    const svalue *bound_summary_sval\n-\t      = compound_summary_sval->get_map ().get (key);\n+\t    const bit_range &summary_bits = iter_summary.first;\n+\t    const svalue *bound_summary_sval = iter_summary.second;\n \t    const svalue *caller_sval\n \t      = convert_svalue_from_summary (bound_summary_sval);\n \t    if (!caller_sval)\n@@ -442,31 +435,26 @@ call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval)\n \t    if (const compound_svalue *inner_compound_sval\n \t\t= caller_sval->dyn_cast_compound_svalue ())\n \t      {\n-\t\tconst concrete_binding *outer_key\n-\t\t  = as_a <const concrete_binding *> (key);\n+\t\tconst bit_range &outer_key = summary_bits;\n \t\tfor (auto inner_kv : *inner_compound_sval)\n \t\t  {\n \t\t    // These should already be mapped to the caller.\n-\t\t    const binding_key *inner_key = inner_kv.m_key;\n-\t\t    const svalue *inner_sval = inner_kv.m_sval;\n-\t\t    gcc_assert (inner_key->concrete_p ());\n-\t\t    const concrete_binding *concrete_key\n-\t\t      = as_a <const concrete_binding *> (inner_key);\n+\t\t    const bit_range &inner_key = inner_kv.first;\n+\t\t    const svalue *inner_sval = inner_kv.second;\n \t\t    bit_offset_t effective_start\n-\t\t      = (concrete_key->get_start_bit_offset ()\n-\t\t\t + outer_key->get_start_bit_offset ());\n-\t\t    const concrete_binding *effective_concrete_key\n-\t\t      = store_mgr->get_concrete_binding\n-\t\t\t  (effective_start,\n-\t\t\t   concrete_key->get_size_in_bits ());\n-\t\t    caller_map.put (effective_concrete_key, inner_sval);\n+\t\t      = (inner_key.get_start_bit_offset ()\n+\t\t\t + outer_key.get_start_bit_offset ());\n+\t\t    const bit_range effective_concrete_key\n+\t\t      (effective_start,\n+\t\t       summary_bits.m_size_in_bits);\n+\t\t    caller_map.insert (effective_concrete_key, inner_sval);\n \t\t  }\n \t      }\n \t    else\n-\t      caller_map.put (key, caller_sval);\n+\t      caller_map.insert (summary_bits, caller_sval);\n \t  }\n \treturn mgr->get_or_create_compound_svalue (summary_sval->get_type (),\n-\t\t\t\t\t\t   caller_map);\n+\t\t\t\t\t\t   std::move (caller_map));\n       }\n       break;\n     case SK_CONJURED:\ndiff --git a/gcc/analyzer/infinite-recursion.cc b/gcc/analyzer/infinite-recursion.cc\nindex 64eeb95de66..c1dc6e49b9b 100644\n--- a/gcc/analyzer/infinite-recursion.cc\n+++ b/gcc/analyzer/infinite-recursion.cc\n@@ -396,7 +396,7 @@ contains_unknown_p (const svalue *sval)\n   if (const compound_svalue *compound_sval\n \t= sval->dyn_cast_compound_svalue ())\n     for (auto iter : *compound_sval)\n-      if (iter.m_sval->get_kind () == SK_UNKNOWN)\n+      if (iter.second->get_kind () == SK_UNKNOWN)\n \treturn true;\n   return false;\n }\ndiff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc\nindex 08d6b559950..5ebaafb2661 100644\n--- a/gcc/analyzer/program-state.cc\n+++ b/gcc/analyzer/program-state.cc\n@@ -565,7 +565,7 @@ sm_state_map::impl_set_state (const svalue *sval,\n \tfor (auto iter = compound_sval->begin ();\n \t     iter != compound_sval->end (); ++iter)\n \t  {\n-\t    const svalue *inner_sval = iter.get_svalue ();\n+\t    const svalue *inner_sval = iter->second;\n \t    if (inner_sval->can_have_associated_state_p ())\n \t      impl_set_state (inner_sval, state, origin, ext_state);\n \t  }\ndiff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc\nindex c4f91ecd688..ba7f9db507b 100644\n--- a/gcc/analyzer/region-model-manager.cc\n+++ b/gcc/analyzer/region-model-manager.cc\n@@ -639,7 +639,7 @@ maybe_undo_optimize_bit_field_compare (tree type,\n   if (!INTEGRAL_TYPE_P (type))\n     return nullptr;\n \n-  const binding_map &map = compound_sval->get_map ();\n+  const concrete_binding_map &map = compound_sval->get_concrete_bindings ();\n   unsigned HOST_WIDE_INT mask = TREE_INT_CST_LOW (cst);\n   /* If \"mask\" is a contiguous range of set bits, see if the\n      compound_sval has a value for those bits.  */\n@@ -651,11 +651,13 @@ maybe_undo_optimize_bit_field_compare (tree type,\n   if (BYTES_BIG_ENDIAN)\n     bound_bits = bit_range (BITS_PER_UNIT - bits.get_next_bit_offset (),\n \t\t\t    bits.m_size_in_bits);\n-  const concrete_binding *conc\n-    = get_store_manager ()->get_concrete_binding (bound_bits);\n-  const svalue *sval = map.get (conc);\n+  const svalue *sval = map.get_any_exact_binding (bound_bits);\n   if (!sval)\n-    return nullptr;\n+    {\n+      /* In theory we could also look for bindings that straddle the\n+\t bit range.  For simplicity, bail out on this case.  */\n+      return nullptr;\n+    }\n \n   /* We have a value;\n      shift it by the correct number of bits.  */\n@@ -1406,7 +1408,26 @@ get_or_create_widening_svalue (tree type,\n \n const svalue *\n region_model_manager::get_or_create_compound_svalue (tree type,\n-\t\t\t\t\t\t     const binding_map &map)\n+\t\t\t\t\t\t     concrete_binding_map &&map)\n+{\n+  compound_svalue::key_t tmp_key (type, &map);\n+  if (compound_svalue **slot = m_compound_values_map.get (tmp_key))\n+    return *slot;\n+  compound_svalue *compound_sval\n+    = new compound_svalue (alloc_symbol_id (), type, std::move (map));\n+  RETURN_UNKNOWN_IF_TOO_COMPLEX (compound_sval);\n+  /* Use make_key rather than reusing the key, so that we use a\n+     ptr to compound_sval's binding_map, rather than the MAP param.  */\n+  m_compound_values_map.put (compound_sval->make_key (), compound_sval);\n+  return compound_sval;\n+}\n+\n+/* Return the svalue * of type TYPE for the compound values in MAP,\n+   creating it if necessary.  */\n+\n+const svalue *\n+region_model_manager::get_or_create_compound_svalue (tree type,\n+\t\t\t\t\t\t     const concrete_binding_map &map)\n {\n   compound_svalue::key_t tmp_key (type, &map);\n   if (compound_svalue **slot = m_compound_values_map.get (tmp_key))\ndiff --git a/gcc/analyzer/region-model-manager.h b/gcc/analyzer/region-model-manager.h\nindex 44122ec05f6..02b5125c435 100644\n--- a/gcc/analyzer/region-model-manager.h\n+++ b/gcc/analyzer/region-model-manager.h\n@@ -77,7 +77,9 @@ public:\n \t\t\t\t\t       const svalue *base_svalue,\n \t\t\t\t\t       const svalue *iter_svalue);\n   const svalue *get_or_create_compound_svalue (tree type,\n-\t\t\t\t\t       const binding_map &map);\n+\t\t\t\t\t       concrete_binding_map &&map);\n+  const svalue *get_or_create_compound_svalue (tree type,\n+\t\t\t\t\t       const concrete_binding_map &map);\n   const svalue *get_or_create_conjured_svalue (tree type, const gimple *stmt,\n \t\t\t\t\t       const region *id_reg,\n \t\t\t\t\t       const conjured_purge &p,\ndiff --git a/gcc/analyzer/region-model-reachability.cc b/gcc/analyzer/region-model-reachability.cc\nindex 19b96b98280..e9e8edfcc1a 100644\n--- a/gcc/analyzer/region-model-reachability.cc\n+++ b/gcc/analyzer/region-model-reachability.cc\n@@ -172,7 +172,7 @@ reachable_regions::handle_sval (const svalue *sval)\n       for (auto iter = compound_sval->begin ();\n \t   iter != compound_sval->end (); ++iter)\n \t{\n-\t  const svalue *iter_sval = iter.get_svalue ();\n+\t  const svalue *iter_sval = iter->second;\n \t  handle_sval (iter_sval);\n \t}\n     }\n@@ -238,7 +238,7 @@ reachable_regions::handle_parm (const svalue *sval, tree param_type)\n       for (auto iter = compound_sval->begin ();\n \t   iter != compound_sval->end (); ++iter)\n \t{\n-\t  const svalue *iter_sval = iter.get_svalue ();\n+\t  const svalue *iter_sval = iter->second;\n \t  handle_sval (iter_sval);\n \t}\n     }\ndiff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc\nindex d46b130b00d..6e25029d9d2 100644\n--- a/gcc/analyzer/region-model.cc\n+++ b/gcc/analyzer/region-model.cc\n@@ -4701,7 +4701,6 @@ region_model::scan_for_null_terminator_1 (const region *reg,\n \t\t\t\t\t  region_model_context *ctxt) const\n {\n   logger *logger = ctxt ? ctxt->get_logger () : nullptr;\n-  store_manager *store_mgr = m_mgr->get_store_manager ();\n \n   region_offset offset = reg->get_offset (m_mgr);\n   if (offset.symbolic_p ())\n@@ -4764,7 +4763,7 @@ region_model::scan_for_null_terminator_1 (const region *reg,\n       logger->end_log_line ();\n     }\n \n-  binding_map result (*store_mgr);\n+  concrete_binding_map result;\n \n   while (1)\n     {\n@@ -4809,9 +4808,7 @@ region_model::scan_for_null_terminator_1 (const region *reg,\n \t  if (out_sval)\n \t    {\n \t      byte_range bytes_to_write (dst_byte_offset, fragment_bytes_read);\n-\t      const binding_key *key\n-\t\t= store_mgr->get_concrete_binding (bytes_to_write);\n-\t      result.put (key, sval);\n+\t      result.insert (bytes_to_write, sval);\n \t    }\n \n \t  src_byte_offset += fragment_bytes_read;\n@@ -4821,7 +4818,7 @@ region_model::scan_for_null_terminator_1 (const region *reg,\n \t    {\n \t      if (out_sval)\n \t\t*out_sval = m_mgr->get_or_create_compound_svalue (NULL_TREE,\n-\t\t\t\t\t\t\t\t  result);\n+\t\t\t\t\t\t\t\t  std::move (result));\n \t      if (logger)\n \t\tlogger->log (\"got terminator\");\n \t      return m_mgr->get_or_create_int_cst (size_type_node,\n@@ -7203,7 +7200,7 @@ private:\n \t    = as_a <const compound_svalue *> (m_copied_sval);\n \t  bit_size_t result = 0;\n \t  /* Find keys for uninit svals.  */\n-\t  for (auto iter : compound_sval->get_map ().get_concrete_bindings ())\n+\t  for (auto iter : compound_sval->get_concrete_bindings ())\n \t    {\n \t      const svalue *sval = iter.second;\n \t      if (const poisoned_svalue *psval\n@@ -7252,7 +7249,7 @@ private:\n       {\n \t/* Find keys for uninit svals.  */\n \tauto_vec<bit_range> uninit_bit_ranges;\n-\tfor (auto iter : compound_sval->get_map ().get_concrete_bindings ())\n+\tfor (auto iter : compound_sval->get_concrete_bindings ())\n \t  {\n \t    const svalue *sval = iter.second;\n \t    if (const poisoned_svalue *psval\n@@ -7462,7 +7459,7 @@ contains_uninit_p (const svalue *sval)\n \tfor (auto iter = compound_sval->begin ();\n \t     iter != compound_sval->end (); ++iter)\n \t  {\n-\t    const svalue *inner_sval = iter.get_svalue ();\n+\t    const svalue *inner_sval = iter->second;\n \t    if (const poisoned_svalue *psval\n \t\t= inner_sval->dyn_cast_poisoned_svalue ())\n \t      if (psval->get_poison_kind () == poison_kind::uninit)\ndiff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc\nindex 5631c4e87e0..b03c79ffa72 100644\n--- a/gcc/analyzer/region.cc\n+++ b/gcc/analyzer/region.cc\n@@ -1713,15 +1713,15 @@ const svalue *\n decl_region::calc_svalue_for_constructor (tree ctor,\n \t\t\t\t\t  region_model_manager *mgr) const\n {\n-  /* Create a binding map, applying ctor to it, using this\n+  /* Create a concrete_binding_map, applying ctor to it, using this\n      decl_region as the base region when building child regions\n      for offset calculations.  */\n-  binding_map map (*mgr->get_store_manager ());\n+  concrete_binding_map map;\n   if (!map.apply_ctor_to_region (this, ctor, mgr))\n     return mgr->get_or_create_unknown_svalue (get_type ());\n \n   /* Return a compound svalue for the map we built.  */\n-  return mgr->get_or_create_compound_svalue (get_type (), map);\n+  return mgr->get_or_create_compound_svalue (get_type (), std::move (map));\n }\n \n /* Get an svalue for CTOR, a CONSTRUCTOR for this region's decl.  */\n@@ -1778,7 +1778,7 @@ decl_region::get_svalue_for_initializer (region_model_manager *mgr) const\n       binding_cluster c (*mgr->get_store_manager (), this);\n       c.zero_fill_region (mgr->get_store_manager (), this);\n       return mgr->get_or_create_compound_svalue (TREE_TYPE (m_decl),\n-\t\t\t\t\t\t c.get_map ());\n+\t\t\t\t\t\t c.get_map ().get_concrete_bindings ());\n     }\n \n   /* LTO can write out error_mark_node as the DECL_INITIAL for simple scalar\ndiff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc\nindex 878536eb42b..d198aec9d2b 100644\n--- a/gcc/analyzer/store.cc\n+++ b/gcc/analyzer/store.cc\n@@ -618,6 +618,234 @@ simplify_for_binding (const svalue *sval)\n   return sval;\n }\n \n+/* class concrete_binding_map.  */\n+\n+/* Dump a representation of this concrete_binding_map to PP.\n+   SIMPLE controls how values and regions are to be printed.\n+   If MULTILINE, then split the dump over multiple lines and\n+   use whitespace for readability, otherwise put all on one line.  */\n+\n+void\n+concrete_binding_map::dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const\n+{\n+  bool first = true;\n+  for (auto iter : *this)\n+    {\n+      const bit_range &bits = iter.first;\n+      const svalue *value = iter.second;\n+      if (multiline)\n+\t{\n+\t  pp_string (pp, \"    bits:   {\");\n+\t  bits.dump_to_pp (pp);\n+\t  pp_string (pp, \"}\");\n+\t  pp_newline (pp);\n+\t  pp_string (pp, \"    value: \");\n+\t  if (tree t = value->get_type ())\n+\t    dump_quoted_tree (pp, t);\n+\t  pp_string (pp, \" {\");\n+\t  value->dump_to_pp (pp, simple);\n+\t  pp_string (pp, \"}\");\n+\t  pp_newline (pp);\n+\t}\n+      else\n+\t{\n+\t  if (first)\n+\t    first = false;\n+\t  else\n+\t    pp_string (pp, \", \");\n+\t  pp_string (pp, \"bits: {\");\n+\t  bits.dump_to_pp (pp);\n+\t  pp_string (pp, \"}, value: {\");\n+\t  value->dump_to_pp (pp, simple);\n+\t  pp_string (pp, \"}\");\n+\t}\n+    }\n+}\n+\n+void\n+concrete_binding_map::dump (bool simple) const\n+{\n+  tree_dump_pretty_printer pp (stderr);\n+  dump_to_pp (&pp, simple, true);\n+  pp_newline (&pp);\n+}\n+\n+void\n+concrete_binding_map::add_to_tree_widget (text_art::tree_widget &parent_widget,\n+\t\t\t\t\t  const text_art::dump_widget_info &dwi) const\n+{\n+  for (auto iter : *this)\n+    {\n+      const bit_range &bits = iter.first;\n+      const svalue *sval = iter.second;\n+\n+      pretty_printer the_pp;\n+      pretty_printer * const pp = &the_pp;\n+      pp_format_decoder (pp) = default_tree_printer;\n+      pp_show_color (pp) = true;\n+      const bool simple = true;\n+\n+      bits.dump_to_pp (pp);\n+      pp_string (pp, \": \");\n+      if (tree t = sval->get_type ())\n+\tdump_quoted_tree (pp, t);\n+      pp_string (pp, \" {\");\n+      sval->dump_to_pp (pp, simple);\n+      pp_string (pp, \"}\");\n+\n+      parent_widget.add_child (text_art::tree_widget::make (dwi, pp));\n+    }\n+}\n+\n+void\n+concrete_binding_map::validate () const\n+{\n+  for (auto iter = m_map.begin (); iter != m_map.end (); ++iter)\n+    {\n+      /* Ensure we don't nest compound svalues\n+\t within concrete_binding_map instances.  */\n+      const svalue *sval = iter->second;\n+      gcc_assert (sval->get_kind () != SK_COMPOUND);\n+\n+      /* Check for overlapping concrete bindings.  */\n+      auto next (iter);\n+      ++next;\n+      if (next != m_map.end ())\n+\t{\n+\t  /* Verify they are in order, and do not overlap.  */\n+\t  const bit_range &iter_bits = iter->first;\n+\t  const bit_range &next_bits = next->first;\n+\t  gcc_assert (iter_bits.get_start_bit_offset ()\n+\t\t      < next_bits.get_start_bit_offset ());\n+\t  gcc_assert (iter_bits.get_last_bit_offset ()\n+\t\t      < next_bits.get_start_bit_offset ());\n+\t  gcc_assert (iter_bits.get_next_bit_offset ()\n+\t\t      <= next_bits.get_start_bit_offset ());\n+\t}\n+    }\n+}\n+\n+/* Comparator for imposing an order on concrete_binding_map instances.  */\n+\n+int\n+concrete_binding_map::cmp (const concrete_binding_map &map1,\n+\t\t\t   const concrete_binding_map &map2)\n+{\n+  if (int size_cmp = map1.size () - map2.size ())\n+    return size_cmp;\n+\n+  auto iter1 = map1.begin ();\n+  auto iter2 = map2.begin ();\n+  while (iter1 != map1.end ())\n+    {\n+      gcc_assert (iter2 != map2.end ());\n+      if (int bit_cmp = bit_range::cmp (iter1->first, iter2->first))\n+\treturn bit_cmp;\n+      if (int sval_cmp = svalue::cmp_ptr (iter1->second, iter2->second))\n+\treturn sval_cmp;\n+      ++iter1;\n+      ++iter2;\n+    }\n+  return 0;\n+}\n+\n+/* Get the svalue bound precisely to BITS, if any, otherwise\n+   return nullptr.  */\n+\n+const svalue *\n+concrete_binding_map::get_any_exact_binding (const bit_range &bits) const\n+{\n+  auto iter = m_map.find (bits);\n+  if (iter == m_map.end ())\n+    return nullptr;\n+  return iter->second;\n+}\n+\n+/* Calculate what the complexity of a compound_svalue instance for this\n+   map will be, based on the bound svalues.  */\n+\n+complexity\n+concrete_binding_map::calc_complexity () const\n+{\n+  unsigned num_child_nodes = 0;\n+  unsigned max_child_depth = 0;\n+  for (auto iter : m_map)\n+    {\n+      const complexity &sval_c = iter.second->get_complexity ();\n+      num_child_nodes += sval_c.m_num_nodes;\n+      max_child_depth = MAX (max_child_depth, sval_c.m_max_depth);\n+    }\n+  return complexity (num_child_nodes + 1, max_child_depth + 1);\n+}\n+\n+void\n+concrete_binding_map::\n+remove_overlapping_binding (store_manager *mgr,\n+\t\t\t    const bit_range &bits_to_drop,\n+\t\t\t    const bit_range &affected_bound_bits,\n+\t\t\t    const svalue &old_sval)\n+{\n+  gcc_assert (bits_to_drop.intersects_p (affected_bound_bits));\n+\n+  /* Remove existing binding.  */\n+  erase (affected_bound_bits);\n+\n+  if (affected_bound_bits.get_start_bit_offset ()\n+      < bits_to_drop.get_start_bit_offset ())\n+    {\n+      /* We have a truncated prefix.  */\n+      bit_range prefix_bits (affected_bound_bits.get_start_bit_offset (),\n+\t\t\t     (bits_to_drop.get_start_bit_offset ()\n+\t\t\t      - affected_bound_bits.get_start_bit_offset ()));\n+      bit_range rel_prefix (0, prefix_bits.m_size_in_bits);\n+      const svalue *prefix_sval\n+\t= old_sval.extract_bit_range (NULL_TREE,\n+\t\t\t\t      rel_prefix,\n+\t\t\t\t      mgr->get_svalue_manager ());\n+      insert (prefix_bits, prefix_sval);\n+    }\n+\n+  if (affected_bound_bits.get_next_bit_offset ()\n+      > bits_to_drop.get_next_bit_offset ())\n+    {\n+      /* We have a truncated suffix.  */\n+      bit_range suffix_bits (bits_to_drop.get_next_bit_offset (),\n+\t\t\t     (affected_bound_bits.get_next_bit_offset ()\n+\t\t\t      - bits_to_drop.get_next_bit_offset ()));\n+      bit_range rel_suffix (bits_to_drop.get_next_bit_offset ()\n+\t\t\t    - affected_bound_bits.get_start_bit_offset (),\n+\t\t\t    suffix_bits.m_size_in_bits);\n+      const svalue *suffix_sval\n+\t= old_sval.extract_bit_range (NULL_TREE,\n+\t\t\t\t      rel_suffix,\n+\t\t\t\t      mgr->get_svalue_manager ());\n+      insert (suffix_bits, suffix_sval);\n+    }\n+}\n+\n+void\n+concrete_binding_map::\n+remove_overlapping_bindings (store_manager *mgr,\n+\t\t\t     const bit_range &bits_to_drop)\n+{\n+  auto bindings = get_overlapping_bindings (bits_to_drop);\n+  for (auto iter : bindings)\n+    remove_overlapping_binding (mgr, bits_to_drop, iter.first, iter.second);\n+}\n+\n+std::vector<std::pair<bit_range, const svalue &>>\n+concrete_binding_map::get_overlapping_bindings (const bit_range &bits)\n+{\n+  std::vector<std::pair<bit_range, const svalue &>> result;\n+  for (auto iter : m_map)\n+    if (iter.first.intersects_p (bits))\n+      {\n+\tgcc_assert (iter.second);\n+\tresult.push_back ({iter.first, *iter.second});\n+      }\n+  return result;\n+}\n+\n /* class binding_map::const_iterator.  */\n \n bool\n@@ -838,7 +1066,7 @@ binding_map::put (const binding_key *key, const svalue *sval)\n       if (iter != m_concrete.end ())\n \t(*iter).second = sval;\n       else\n-\tm_concrete.insert ({bits, sval});\n+\tm_concrete.insert (bits, sval);\n     }\n }\n \n@@ -974,24 +1202,7 @@ binding_map::validate () const\n   /* We can't have both concrete and symbolic keys.  */\n   gcc_assert (num_concrete == 0 || num_symbolic == 0);\n \n-  /* Check for overlapping concrete bindings.  */\n-  for (auto iter = m_concrete.begin (); iter != m_concrete.end (); ++iter)\n-    {\n-      auto next (iter);\n-      ++next;\n-      if (next != m_concrete.end ())\n-\t{\n-\t  /* Verify they are in order, and do not overlap.  */\n-\t  const bit_range &iter_bits = iter->first;\n-\t  const bit_range &next_bits = next->first;\n-\t  gcc_assert (iter_bits.get_start_bit_offset ()\n-\t\t      < next_bits.get_start_bit_offset ());\n-\t  gcc_assert (iter_bits.get_last_bit_offset ()\n-\t\t      < next_bits.get_start_bit_offset ());\n-\t  gcc_assert (iter_bits.get_next_bit_offset ()\n-\t\t      <= next_bits.get_start_bit_offset ());\n-\t}\n-    }\n+  m_concrete.validate ();\n }\n \n /* Return a new json::object of the form\n@@ -1052,39 +1263,6 @@ binding_map::add_to_tree_widget (text_art::tree_widget &parent_widget,\n     }\n }\n \n-\n-/* Comparator for imposing an order on binding_maps.  */\n-\n-int\n-binding_map::cmp (const binding_map &map1, const binding_map &map2)\n-{\n-  if (int count_cmp = map1.elements () - map2.elements ())\n-    return count_cmp;\n-\n-  auto_vec <const binding_key *> keys1 (map1.elements ());\n-  for (auto iter : map1)\n-    keys1.quick_push (iter.m_key);\n-  keys1.qsort (binding_key::cmp_ptrs);\n-\n-  auto_vec <const binding_key *> keys2 (map2.elements ());\n-  for (auto iter : map2)\n-    keys2.quick_push (iter.m_key);\n-  keys2.qsort (binding_key::cmp_ptrs);\n-\n-  for (size_t i = 0; i < keys1.length (); i++)\n-    {\n-      const binding_key *k1 = keys1[i];\n-      const binding_key *k2 = keys2[i];\n-      if (int key_cmp = binding_key::cmp (k1, k2))\n-\treturn key_cmp;\n-      gcc_assert (k1 == k2);\n-      if (int sval_cmp = svalue::cmp_ptr (map1.get (k1), map2.get (k2)))\n-\treturn sval_cmp;\n-    }\n-\n-  return 0;\n-}\n-\n /* Get the child region of PARENT_REG based upon INDEX within a\n    CONSTRUCTOR.   */\n \n@@ -1157,8 +1335,8 @@ get_svalue_for_ctor_val (tree val, region_model_manager *mgr)\n    to hitting a complexity limit).  */\n \n bool\n-binding_map::apply_ctor_to_region (const region *parent_reg, tree ctor,\n-\t\t\t\t   region_model_manager *mgr)\n+concrete_binding_map::apply_ctor_to_region (const region *parent_reg, tree ctor,\n+\t\t\t\t\t    region_model_manager *mgr)\n {\n   gcc_assert (parent_reg);\n   gcc_assert (TREE_CODE (ctor) == CONSTRUCTOR);\n@@ -1218,10 +1396,10 @@ binding_map::apply_ctor_to_region (const region *parent_reg, tree ctor,\n    to hitting a complexity limit).  */\n \n bool\n-binding_map::apply_ctor_val_to_range (const region *parent_reg,\n-\t\t\t\t      region_model_manager *mgr,\n-\t\t\t\t      tree min_index, tree max_index,\n-\t\t\t\t      tree val)\n+concrete_binding_map::apply_ctor_val_to_range (const region *parent_reg,\n+\t\t\t\t\t       region_model_manager *mgr,\n+\t\t\t\t\t       tree min_index, tree max_index,\n+\t\t\t\t\t       tree val)\n {\n   gcc_assert (TREE_CODE (min_index) == INTEGER_CST);\n   gcc_assert (TREE_CODE (max_index) == INTEGER_CST);\n@@ -1245,10 +1423,6 @@ binding_map::apply_ctor_val_to_range (const region *parent_reg,\n     = max_element_key->dyn_cast_concrete_binding ();\n   bit_size_t range_size_in_bits\n     = max_element_ckey->get_next_bit_offset () - start_bit_offset;\n-  const concrete_binding *range_key\n-    = smgr->get_concrete_binding (start_bit_offset, range_size_in_bits);\n-  if (range_key->symbolic_p ())\n-    return false;\n \n   /* Get the value.  */\n   if (TREE_CODE (val) == CONSTRUCTOR)\n@@ -1256,7 +1430,8 @@ binding_map::apply_ctor_val_to_range (const region *parent_reg,\n   const svalue *sval = get_svalue_for_ctor_val (val, mgr);\n \n   /* Bind the value to the range.  */\n-  put (range_key, sval);\n+  const bit_range affected_bits (start_bit_offset, range_size_in_bits);\n+  insert (affected_bits, sval);\n   return true;\n }\n \n@@ -1266,9 +1441,9 @@ binding_map::apply_ctor_val_to_range (const region *parent_reg,\n    to hitting a complexity limit).  */\n \n bool\n-binding_map::apply_ctor_pair_to_child_region (const region *parent_reg,\n-\t\t\t\t\t      region_model_manager *mgr,\n-\t\t\t\t\t      tree index, tree val)\n+concrete_binding_map::apply_ctor_pair_to_child_region (const region *parent_reg,\n+\t\t\t\t\t\t       region_model_manager *mgr,\n+\t\t\t\t\t\t       tree index, tree val)\n {\n   const region *child_reg\n     = get_subregion_within_ctor_for_ctor_pair (parent_reg, index, val, mgr);\n@@ -1309,7 +1484,8 @@ binding_map::apply_ctor_pair_to_child_region (const region *parent_reg,\n \t    (child_parent_offset, sval_bit_size);\n \t}\n       gcc_assert (k->concrete_p ());\n-      put (k, sval);\n+      auto conc_key = as_a <const concrete_binding *> (k);\n+      insert (conc_key->get_bit_range (), sval);\n       return true;\n     }\n }\n@@ -1477,42 +1653,9 @@ binding_map::remove_overlapping_bindings (store_manager *mgr,\n \n \t    const bit_range &drop_bits = drop_ckey->get_bit_range ();\n \t    const bit_range &iter_bits = iter_ckey->get_bit_range ();\n-\n-\t    if (iter_bits.get_start_bit_offset ()\n-\t\t  < drop_bits.get_start_bit_offset ())\n-\t      {\n-\t\t/* We have a truncated prefix.  */\n-\t\tbit_range prefix_bits (iter_bits.get_start_bit_offset (),\n-\t\t\t\t       (drop_bits.get_start_bit_offset ()\n-\t\t\t\t\t- iter_bits.get_start_bit_offset ()));\n-\t\tconst concrete_binding *prefix_key\n-\t\t  = mgr->get_concrete_binding (prefix_bits);\n-\t\tbit_range rel_prefix (0, prefix_bits.m_size_in_bits);\n-\t\tconst svalue *prefix_sval\n-\t\t  = old_sval->extract_bit_range (NULL_TREE,\n-\t\t\t\t\t\t rel_prefix,\n-\t\t\t\t\t\t mgr->get_svalue_manager ());\n-\t\tput (prefix_key, prefix_sval);\n-\t      }\n-\n-\t    if (iter_bits.get_next_bit_offset ()\n-\t\t  > drop_bits.get_next_bit_offset ())\n-\t      {\n-\t\t/* We have a truncated suffix.  */\n-\t\tbit_range suffix_bits (drop_bits.get_next_bit_offset (),\n-\t\t\t\t       (iter_bits.get_next_bit_offset ()\n-\t\t\t\t\t- drop_bits.get_next_bit_offset ()));\n-\t\tconst concrete_binding *suffix_key\n-\t\t  = mgr->get_concrete_binding (suffix_bits);\n-\t\tbit_range rel_suffix (drop_bits.get_next_bit_offset ()\n-\t\t\t\t\t- iter_bits.get_start_bit_offset (),\n-\t\t\t\t      suffix_bits.m_size_in_bits);\n-\t\tconst svalue *suffix_sval\n-\t\t  = old_sval->extract_bit_range (NULL_TREE,\n-\t\t\t\t\t\t rel_suffix,\n-\t\t\t\t\t\t mgr->get_svalue_manager ());\n-\t\tput (suffix_key, suffix_sval);\n-\t      }\n+\t    gcc_assert (old_sval);\n+\t    m_concrete.remove_overlapping_binding (mgr, drop_bits, iter_bits,\n+\t\t\t\t\t\t   *old_sval);\n \t  }\n     }\n }\n@@ -1753,22 +1896,15 @@ binding_cluster::bind_compound_sval (store_manager *mgr,\n \n   for (auto iter : *compound_sval)\n     {\n-      const binding_key *iter_key = iter.m_key;\n-      const svalue *iter_sval = iter.m_sval;\n-\n-      if (const concrete_binding *concrete_key\n-\t  = iter_key->dyn_cast_concrete_binding ())\n-\t{\n-\t  bit_offset_t effective_start\n-\t    = (concrete_key->get_start_bit_offset ()\n-\t       + reg_offset.get_bit_offset ());\n-\t  const concrete_binding *effective_concrete_key\n-\t    = mgr->get_concrete_binding (effective_start,\n-\t\t\t\t\t concrete_key->get_size_in_bits ());\n-\t  bind_key (effective_concrete_key, iter_sval);\n-\t}\n-      else\n-\tgcc_unreachable ();\n+      const bit_range &iter_bits = iter.first;\n+      const svalue *iter_sval = iter.second;\n+\n+      bit_offset_t effective_start\n+\t= (iter_bits.get_start_bit_offset ()\n+\t   + reg_offset.get_bit_offset ());\n+      const bit_range affected_bits (effective_start,\n+\t\t\t\t     iter_bits.m_size_in_bits);\n+      bind_key (mgr->get_concrete_binding (affected_bits), iter_sval);\n     }\n }\n \n@@ -2060,8 +2196,8 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr,\n      perhaps we should have a spatial-organized data structure for\n      concrete keys, though.  */\n \n-  binding_map result_map (*mgr);\n-  binding_map default_map (*mgr);\n+  concrete_binding_map result_map;\n+  concrete_binding_map default_map;\n \n   /* Set up default values in default_map.  */\n   const svalue *default_sval;\n@@ -2077,10 +2213,10 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr,\n     = default_key->dyn_cast_concrete_binding ();\n   if (!concrete_default_key)\n     return nullptr;\n-  const concrete_binding *default_key_relative_to_reg\n-     = mgr->get_concrete_binding (0, concrete_default_key->get_size_in_bits ());\n+  const bit_range default_key_relative_to_reg\n+     (0, concrete_default_key->get_size_in_bits ());\n \n-  default_map.put (default_key_relative_to_reg, default_sval);\n+  default_map.insert (default_key_relative_to_reg, default_sval);\n \n   for (auto iter : m_map)\n     {\n@@ -2111,19 +2247,12 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr,\n \t  if (reg_range.contains_p (bound_range, &subrange))\n \t    {\n \t      /* We have a bound range fully within REG.\n-\t\t Add it to map, offsetting accordingly.  */\n-\n-\t      /* Get offset of KEY relative to REG, rather than to\n-\t\t the cluster.  */\n-\t      const concrete_binding *offset_concrete_key\n-\t\t= mgr->get_concrete_binding (subrange);\n-\t      result_map.put (offset_concrete_key, sval);\n+\t\t Add it to result_map, offsetting accordingly.  */\n+\t      result_map.insert (subrange, sval);\n \n \t      /* Clobber default_map, removing/trimming/spliting where\n \t\t it overlaps with offset_concrete_key.  */\n-\t      default_map.remove_overlapping_bindings (mgr,\n-\t\t\t\t\t\t       offset_concrete_key,\n-\t\t\t\t\t\t       nullptr, nullptr, false);\n+\t      default_map.remove_overlapping_bindings (mgr, subrange);\n \t    }\n \t  else if (bound_range.contains_p (reg_range, &subrange))\n \t    {\n@@ -2148,16 +2277,11 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr,\n \t\t\t\t\t   bound_subrange,\n \t\t\t\t\t   mgr->get_svalue_manager ());\n \n-\t      /* Get key for overlap, relative to the REG.  */\n-\t      const concrete_binding *overlap_concrete_key\n-\t\t= mgr->get_concrete_binding (reg_subrange);\n-\t      result_map.put (overlap_concrete_key, overlap_sval);\n+\t      result_map.insert (reg_subrange, overlap_sval);\n \n \t      /* Clobber default_map, removing/trimming/spliting where\n \t\t it overlaps with overlap_concrete_key.  */\n-\t      default_map.remove_overlapping_bindings (mgr,\n-\t\t\t\t\t\t       overlap_concrete_key,\n-\t\t\t\t\t\t       nullptr, nullptr, false);\n+\t      default_map.remove_overlapping_bindings (mgr, reg_subrange);\n \t    }\n \t}\n       else\n@@ -2165,18 +2289,19 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr,\n \treturn nullptr;\n     }\n \n-  if (result_map.elements () == 0)\n+  if (result_map.size () == 0)\n     return nullptr;\n \n   /* Merge any bindings from default_map into result_map.  */\n   for (auto iter : default_map)\n     {\n-      const binding_key *key = iter.m_key;\n-      const svalue *sval = iter.m_sval;\n-      result_map.put (key, sval);\n+      const bit_range &key = iter.first;\n+      const svalue *sval = iter.second;\n+      result_map.insert (key, sval);\n     }\n \n-  return sval_mgr->get_or_create_compound_svalue (reg->get_type (), result_map);\n+  return sval_mgr->get_or_create_compound_svalue (reg->get_type (),\n+\t\t\t\t\t\t  std::move (result_map));\n }\n \n /* Remove, truncate, and/or split any bindings within this map that\n@@ -3845,7 +3970,7 @@ store::replay_call_summary_cluster (call_summary_replay &r,\n \tif (!summary_sval)\n \t  summary_sval = reg_mgr->get_or_create_compound_svalue\n \t    (summary_base_reg->get_type (),\n-\t     summary_cluster->get_map ());\n+\t     summary_cluster->get_map ().get_concrete_bindings ());\n \tconst svalue *caller_sval\n \t  = r.convert_svalue_from_summary (summary_sval);\n \tif (!caller_sval)\ndiff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h\nindex e562e5479ed..021a438222f 100644\n--- a/gcc/analyzer/store.h\n+++ b/gcc/analyzer/store.h\n@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see\n #define GCC_ANALYZER_STORE_H\n \n #include \"text-art/tree-widget.h\"\n+#include \"analyzer/complexity.h\"\n \n /* Implementation of the region-based ternary model described in:\n      \"A Memory Model for Static Analysis of C Programs\"\n@@ -513,11 +514,115 @@ template <> struct default_hash_traits<ana::symbolic_binding>\n \n namespace ana {\n \n-/* A mapping from binding_keys to svalues, for use by binding_cluster\n-   and compound_svalue.\n-   We store a map from concrete keys to svalues, which is ordered by\n-   the start offset.\n-   We also store a vector of (symbolic key, svalue) pairs, but for now\n+/* A mapping from concrete bit_ranges to svalues, for use by\n+   binding_cluster and compound_svalue.\n+   The keys are ordered by the start offset, and must not overlap\n+   The bound svalues may not be compound_svalues, so that we don't\n+   nest these (for canonicalization).  */\n+\n+class concrete_binding_map\n+{\n+public:\n+  using map_t = std::map<bit_range, const svalue *>;\n+  using const_iterator = map_t::const_iterator;\n+  using iterator = map_t::iterator;\n+\n+  void clear () { m_map.clear (); }\n+  bool empty_p () const { return m_map.empty (); }\n+\n+  bool\n+  operator== (const concrete_binding_map &other) const\n+  {\n+    return m_map == other.m_map;\n+  }\n+  bool\n+  operator!= (const concrete_binding_map &other) const\n+  {\n+    return m_map != other.m_map;\n+  }\n+\n+  const_iterator begin () const { return m_map.begin (); }\n+  const_iterator end () const { return m_map.end (); }\n+  iterator begin () { return m_map.begin (); }\n+  iterator end () { return m_map.end (); }\n+\n+  size_t size () const { return m_map.size (); }\n+\n+  void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;\n+  void dump (bool simple) const;\n+\n+  void add_to_tree_widget (text_art::tree_widget &parent_widget,\n+\t\t\t   const text_art::dump_widget_info &dwi) const;\n+\n+  void validate () const;\n+\n+  static int\n+  cmp (const concrete_binding_map &map1,\n+       const concrete_binding_map &map2);\n+\n+  void\n+  insert (const bit_range &bits, const svalue *sval)\n+  {\n+    m_map.insert ({bits, sval});\n+  }\n+\n+  void\n+  insert (const byte_range &bytes, const svalue *sval)\n+  {\n+    m_map.insert ({bytes.as_bit_range (), sval});\n+  }\n+\n+  void\n+  erase (const bit_range &bits)\n+  {\n+    m_map.erase (bits);\n+  }\n+\n+  const svalue *\n+  get_any_exact_binding (const bit_range &bits) const;\n+\n+  const_iterator\n+  find (const bit_range &bits) const\n+  {\n+    return m_map.find (bits);\n+  }\n+  iterator\n+  find (const bit_range &bits)\n+  {\n+    return m_map.find (bits);\n+  }\n+\n+  complexity calc_complexity () const;\n+\n+  bool apply_ctor_to_region (const region *parent_reg, tree ctor,\n+\t\t\t     region_model_manager *mgr);\n+\n+  void remove_overlapping_binding (store_manager *mgr,\n+\t\t\t\t   const bit_range &bits_to_drop,\n+\t\t\t\t   const bit_range &affected_bound_bits,\n+\t\t\t\t   const svalue &old_sval);\n+\n+  void remove_overlapping_bindings (store_manager *mgr,\n+\t\t\t\t    const bit_range &bits);\n+\n+private:\n+  std::vector<std::pair<bit_range, const svalue &>>\n+  get_overlapping_bindings (const bit_range &bits);\n+\n+  bool apply_ctor_val_to_range (const region *parent_reg,\n+\t\t\t\tregion_model_manager *mgr,\n+\t\t\t\ttree min_index, tree max_index,\n+\t\t\t\ttree val);\n+  bool apply_ctor_pair_to_child_region (const region *parent_reg,\n+\t\t\t\t\tregion_model_manager *mgr,\n+\t\t\t\t\ttree index, tree val);\n+\n+  map_t m_map;\n+};\n+\n+/* A mapping from binding_keys to svalues, for use by binding_cluster.\n+   We store bindings at concrete bit ranges via a concrete_binding_map,\n+   along with a vector of (symbolic key, svalue) pairs, but for now\n    this has maximum length of 1.  */\n \n class binding_map\n@@ -534,7 +639,7 @@ public:\n     const region *m_region;\n     const svalue *m_sval;\n   };\n-  using concrete_bindings_t = std::map<bit_range, const svalue *>;\n+  using concrete_bindings_t = concrete_binding_map;\n   using symbolic_bindings_t = std::vector<symbolic_binding>;\n \n   struct binding_pair\n@@ -632,7 +737,7 @@ public:\n \n   bool empty_p () const\n   {\n-    return m_concrete.empty () && m_symbolic.empty ();\n+    return m_concrete.empty_p () && m_symbolic.empty ();\n   }\n \n   const_iterator_t begin () const;\n@@ -651,11 +756,6 @@ public:\n   void add_to_tree_widget (text_art::tree_widget &parent_widget,\n \t\t\t   const text_art::dump_widget_info &dwi) const;\n \n-  bool apply_ctor_to_region (const region *parent_reg, tree ctor,\n-\t\t\t     region_model_manager *mgr);\n-\n-  static int cmp (const binding_map &map1, const binding_map &map2);\n-\n   void remove_overlapping_bindings (store_manager *mgr,\n \t\t\t\t    const binding_key *drop_key,\n \t\t\t\t    uncertainty_t *uncertainty,\n@@ -671,13 +771,6 @@ public:\n private:\n   void get_overlapping_bindings (const binding_key *key,\n \t\t\t\t auto_vec<const binding_key *> *out);\n-  bool apply_ctor_val_to_range (const region *parent_reg,\n-\t\t\t\tregion_model_manager *mgr,\n-\t\t\t\ttree min_index, tree max_index,\n-\t\t\t\ttree val);\n-  bool apply_ctor_pair_to_child_region (const region *parent_reg,\n-\t\t\t\t\tregion_model_manager *mgr,\n-\t\t\t\t\ttree index, tree val);\n \n   store_manager &m_store_mgr;\n   concrete_bindings_t m_concrete;\ndiff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc\nindex 74c2c0dbd4a..52e853fdc8a 100644\n--- a/gcc/analyzer/svalue.cc\n+++ b/gcc/analyzer/svalue.cc\n@@ -678,8 +678,9 @@ svalue::cmp_ptr (const svalue *sval1, const svalue *sval2)\n       {\n \tconst compound_svalue *compound_sval1 = (const compound_svalue *)sval1;\n \tconst compound_svalue *compound_sval2 = (const compound_svalue *)sval2;\n-\treturn binding_map::cmp (compound_sval1->get_map (),\n-\t\t\t\t compound_sval2->get_map ());\n+\treturn concrete_binding_map::cmp\n+\t  (compound_sval1->get_concrete_bindings (),\n+\t   compound_sval2->get_concrete_bindings ());\n       }\n       break;\n     case SK_CONJURED:\n@@ -2349,19 +2350,29 @@ unmergeable_svalue::implicitly_live_p (const svalue_set *live_svalues,\n \n compound_svalue::compound_svalue (symbol::id_t id,\n \t\t\t\t  tree type,\n-\t\t\t\t  const binding_map &map)\n-: svalue (calc_complexity (map), id, type), m_map (map)\n+\t\t\t\t  concrete_binding_map &&map)\n+: svalue (map.calc_complexity (), id, type), m_map (std::move (map))\n {\n #if CHECKING_P\n   for (auto iter : *this)\n     {\n-      /* All keys within the underlying binding_map are required to be concrete,\n-\t not symbolic.  */\n-      const binding_key *key = iter.m_key;\n-      gcc_assert (key->concrete_p ());\n+      /* We don't nest compound svalues.  */\n+      const svalue *sval = iter.second;\n+      gcc_assert (sval->get_kind () != SK_COMPOUND);\n+    }\n+#endif\n+}\n \n+compound_svalue::compound_svalue (symbol::id_t id,\n+\t\t\t\t  tree type,\n+\t\t\t\t  const concrete_binding_map &map)\n+: svalue (map.calc_complexity (), id, type), m_map (map)\n+{\n+#if CHECKING_P\n+  for (auto iter : *this)\n+    {\n       /* We don't nest compound svalues.  */\n-      const svalue *sval = iter.m_sval;\n+      const svalue *sval = iter.second;\n       gcc_assert (sval->get_kind () != SK_COMPOUND);\n     }\n #endif\n@@ -2424,30 +2435,10 @@ void\n compound_svalue::accept (visitor *v) const\n {\n   for (auto iter : m_map)\n-    {\n-      //iter.first.accept (v);\n-      iter.m_sval->accept (v);\n-    }\n+    iter.second->accept (v);\n   v->visit_compound_svalue (this);\n }\n \n-/* Calculate what the complexity of a compound_svalue instance for MAP\n-   will be, based on the svalues bound within MAP.  */\n-\n-complexity\n-compound_svalue::calc_complexity (const binding_map &map)\n-{\n-  unsigned num_child_nodes = 0;\n-  unsigned max_child_depth = 0;\n-  for (auto iter : map)\n-    {\n-      const complexity &sval_c = iter.m_sval->get_complexity ();\n-      num_child_nodes += sval_c.m_num_nodes;\n-      max_child_depth = MAX (max_child_depth, sval_c.m_max_depth);\n-    }\n-  return complexity (num_child_nodes + 1, max_child_depth + 1);\n-}\n-\n /* Implementation of svalue::maybe_fold_bits_within vfunc\n    for compound_svalue.  */\n \n@@ -2456,65 +2447,56 @@ compound_svalue::maybe_fold_bits_within (tree type,\n \t\t\t\t\t const bit_range &bits,\n \t\t\t\t\t region_model_manager *mgr) const\n {\n-  binding_map result_map (*mgr->get_store_manager ());\n+  concrete_binding_map result_map;\n   for (auto iter : m_map)\n     {\n-      const binding_key *key = iter.m_key;\n-      if (const concrete_binding *conc_key\n-\t  = key->dyn_cast_concrete_binding ())\n+      const bit_range &iter_bits = iter.first;\n+\n+      /* Ignore concrete bindings outside BITS.  */\n+      if (!iter_bits.intersects_p (bits))\n+\tcontinue;\n+\n+      const svalue *sval = iter.second;\n+      /* Get the position of iter_bits relative to BITS.  */\n+      bit_range result_location (iter_bits.get_start_bit_offset ()\n+\t\t\t\t - bits.get_start_bit_offset (),\n+\t\t\t\t iter_bits.m_size_in_bits);\n+      /* If iter_bits starts after BITS, trim off leading bits\n+\t from the svalue and adjust binding location.  */\n+      if (result_location.m_start_bit_offset < 0)\n \t{\n-\t  /* Ignore concrete bindings outside BITS.  */\n-\t  if (!conc_key->get_bit_range ().intersects_p (bits))\n-\t    continue;\n-\n-\t  const svalue *sval = iter.m_sval;\n-\t  /* Get the position of conc_key relative to BITS.  */\n-\t  bit_range result_location (conc_key->get_start_bit_offset ()\n-\t\t\t\t     - bits.get_start_bit_offset (),\n-\t\t\t\t     conc_key->get_size_in_bits ());\n-\t  /* If conc_key starts after BITS, trim off leading bits\n-\t     from the svalue and adjust binding location.  */\n-\t  if (result_location.m_start_bit_offset < 0)\n-\t    {\n-\t      bit_size_t leading_bits_to_drop\n-\t\t= -result_location.m_start_bit_offset;\n-\t      result_location = bit_range\n-\t\t(0, result_location.m_size_in_bits - leading_bits_to_drop);\n-\t      bit_range bits_within_sval (leading_bits_to_drop,\n-\t\t\t\t\t  result_location.m_size_in_bits);\n-\t      /* Trim off leading bits from iter_sval.  */\n-\t      sval = mgr->get_or_create_bits_within (NULL_TREE,\n-\t\t\t\t\t\t     bits_within_sval,\n-\t\t\t\t\t\t     sval);\n-\t    }\n-\t  /* If conc_key finishes after BITS, trim off trailing bits\n-\t     from the svalue and adjust binding location.  */\n-\t  if (conc_key->get_next_bit_offset ()\n-\t      > bits.get_next_bit_offset ())\n-\t    {\n-\t      bit_size_t trailing_bits_to_drop\n-\t\t= (conc_key->get_next_bit_offset ()\n-\t\t   - bits.get_next_bit_offset ());\n-\t      result_location = bit_range\n-\t\t(result_location.m_start_bit_offset,\n-\t\t result_location.m_size_in_bits - trailing_bits_to_drop);\n-\t      bit_range bits_within_sval (0,\n-\t\t\t\t\t  result_location.m_size_in_bits);\n-\t      /* Trim off leading bits from iter_sval.  */\n-\t      sval = mgr->get_or_create_bits_within (NULL_TREE,\n-\t\t\t\t\t\t     bits_within_sval,\n-\t\t\t\t\t\t     sval);\n-\t    }\n-\t  const concrete_binding *offset_conc_key\n-\t    = mgr->get_store_manager ()->get_concrete_binding\n-\t\t(result_location);\n-\t  result_map.put (offset_conc_key, sval);\n+\t  bit_size_t leading_bits_to_drop\n+\t    = -result_location.m_start_bit_offset;\n+\t  result_location = bit_range\n+\t    (0, result_location.m_size_in_bits - leading_bits_to_drop);\n+\t  bit_range bits_within_sval (leading_bits_to_drop,\n+\t\t\t\t      result_location.m_size_in_bits);\n+\t  /* Trim off leading bits from iter_sval.  */\n+\t  sval = mgr->get_or_create_bits_within (NULL_TREE,\n+\t\t\t\t\t\t bits_within_sval,\n+\t\t\t\t\t\t sval);\n \t}\n-      else\n-\t/* If we have any symbolic keys we can't get it as bits.  */\n-\treturn nullptr;\n+      /* If iter_bits finishes after BITS, trim off trailing bits\n+\t from the svalue and adjust binding location.  */\n+      if (iter_bits.get_next_bit_offset ()\n+\t  > bits.get_next_bit_offset ())\n+\t{\n+\t  bit_size_t trailing_bits_to_drop\n+\t    = (iter_bits.get_next_bit_offset ()\n+\t       - bits.get_next_bit_offset ());\n+\t  result_location = bit_range\n+\t    (result_location.m_start_bit_offset,\n+\t     result_location.m_size_in_bits - trailing_bits_to_drop);\n+\t  bit_range bits_within_sval (0,\n+\t\t\t\t      result_location.m_size_in_bits);\n+\t  /* Trim off leading bits from iter_sval.  */\n+\t  sval = mgr->get_or_create_bits_within (NULL_TREE,\n+\t\t\t\t\t\t bits_within_sval,\n+\t\t\t\t\t\t sval);\n+\t}\n+      result_map.insert (result_location, sval);\n     }\n-  return mgr->get_or_create_compound_svalue (type, result_map);\n+  return mgr->get_or_create_compound_svalue (type, std::move (result_map));\n }\n \n /* class conjured_svalue : public svalue.  */\ndiff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h\nindex 72b1542d730..a3fc41056c4 100644\n--- a/gcc/analyzer/svalue.h\n+++ b/gcc/analyzer/svalue.h\n@@ -1402,15 +1402,13 @@ template <> struct default_hash_traits<widening_svalue::key_t>\n namespace ana {\n \n /* Concrete subclass of svalue representing a mapping of bit-ranges\n-   to svalues, analogous to a cluster within the store.\n+   to svalues, analogous to a cluster within the store, but without\n+   symbolic keys.\n \n    This is for use in places where we want to represent a store-like\n    mapping, but are required to use an svalue, such as when handling\n    compound assignments and compound return values.\n \n-   All keys within the underlying binding_map are required to be concrete,\n-   not symbolic.\n-\n    Instances of this class shouldn't be bound as-is into the store;\n    instead they should be unpacked.  Similarly, they should not be\n    nested.  */\n@@ -1418,15 +1416,15 @@ namespace ana {\n class compound_svalue : public svalue\n {\n public:\n-  typedef binding_map::const_iterator_t const_iterator_t;\n-  typedef binding_map::iterator_t iterator_t;\n+  typedef concrete_binding_map::const_iterator const_iterator_t;\n+  typedef concrete_binding_map::iterator iterator_t;\n \n   /* A support class for uniquifying instances of compound_svalue.\n-     Note that to avoid copies, keys store pointers to binding_maps,\n-     rather than the maps themselves.  */\n+     Note that to avoid copies, keys store pointers to\n+     concrete_binding_map, rather than the maps themselves.  */\n   struct key_t\n   {\n-    key_t (tree type, const binding_map *map_ptr)\n+    key_t (tree type, const concrete_binding_map *map_ptr)\n     : m_type (type), m_map_ptr (map_ptr)\n     {}\n \n@@ -1450,10 +1448,11 @@ public:\n     bool is_empty () const { return m_type == reinterpret_cast<tree> (2); }\n \n     tree m_type;\n-    const binding_map *m_map_ptr;\n+    const concrete_binding_map *m_map_ptr;\n   };\n \n-  compound_svalue (symbol::id_t id, tree type, const binding_map &map);\n+  compound_svalue (symbol::id_t id, tree type, concrete_binding_map &&map);\n+  compound_svalue (symbol::id_t id, tree type, const concrete_binding_map &map);\n \n   enum svalue_kind get_kind () const final override { return SK_COMPOUND; }\n   const compound_svalue *dyn_cast_compound_svalue () const final override\n@@ -1471,7 +1470,8 @@ public:\n \n   void accept (visitor *v) const final override;\n \n-  const binding_map &get_map () const { return m_map; }\n+  const concrete_binding_map &\n+  get_concrete_bindings () const { return m_map; }\n \n   const_iterator_t begin () const { return m_map.begin (); }\n   const_iterator_t end () const { return m_map.end (); }\n@@ -1489,9 +1489,7 @@ public:\n \t\t\t  region_model_manager *mgr) const final override;\n \n  private:\n-  static complexity calc_complexity (const binding_map &map);\n-\n-  binding_map m_map;\n+  concrete_binding_map m_map;\n };\n \n } // namespace ana\n","prefixes":["pushed:","r17-182"]}