From 1c65ed8ec717f92438ca28c5cd7f0ca139ec4fe0 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Tue, 23 Jan 2024 08:44:01 -0800 Subject: [PATCH] Implement Container V2 for std::deque --- test/integration/std_deque.toml | 24 +++-- test/integration/std_deque_del_allocator.toml | 25 ++++- types/deque_list_type.toml | 92 +++++++++++++++++++ 3 files changed, 133 insertions(+), 8 deletions(-) diff --git a/test/integration/std_deque.toml b/test/integration/std_deque.toml index bf774ef..486a29b 100644 --- a/test/integration/std_deque.toml +++ b/test/integration/std_deque.toml @@ -3,22 +3,21 @@ includes = ["deque"] [cases] [cases.int_empty] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316 param_types = ["const std::deque&"] setup = "return {};" expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}]' + expect_json_v2 = '[{"size":656, "staticSize":80, "exclusiveSize":656, "length":0, "capacity":128}]' [cases.int_some] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316 param_types = ["const std::deque&"] setup = "return {{1,2,3}};" expect_json = '[{"staticSize":80, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}]' + expect_json_v2 = '[{"size":656, "staticSize":80, "exclusiveSize":644, "length":3, "capacity":128}]' [cases.deque_int_empty] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316 param_types = ["const std::deque>&"] setup = "return {};" expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":80}]' + expect_json_v2 = '[{"size":624, "staticSize":80, "exclusiveSize":624, "length":0, "capacity":6}]' [cases.deque_int_some] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316 param_types = ["const std::deque>&"] setup = "return {{{1,2,3},{},{4,5}}};" expect_json = '''[{ @@ -28,7 +27,18 @@ includes = ["deque"] "capacity":3, "elementStaticSize":80, "members":[ - {"staticSize":80, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, - {"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}, - {"staticSize":80, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4} + {"size":0, "staticSize":80, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, + {"size":0, "staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}, + {"size":0, "staticSize":80, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4} + ]}]''' + expect_json_v2 = '''[{ + "size":2352, + "staticSize":80, + "exclusiveSize":384, + "length":3, + "capacity":6, + "members":[ + {"size":656, "staticSize":80, "exclusiveSize":644, "length":3, "capacity":128}, + {"size":656, "staticSize":80, "exclusiveSize":656, "length":0, "capacity":128}, + {"size":656, "staticSize":80, "exclusiveSize":648, "length":2, "capacity":128} ]}]''' diff --git a/test/integration/std_deque_del_allocator.toml b/test/integration/std_deque_del_allocator.toml index 1236364..e582a85 100644 --- a/test/integration/std_deque_del_allocator.toml +++ b/test/integration/std_deque_del_allocator.toml @@ -24,7 +24,6 @@ includes = ["deque"] [cases] [cases.a] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316 param_types = ["const Foo&"] setup = ''' Foo foo; @@ -55,3 +54,27 @@ includes = ["deque"] ]} ]} ]}]''' + expect_json_v2 = '''[{ + "size":1312, + "staticSize":160, + "exclusiveSize":0, + "members":[ + {"name":"v1", "size":656, "staticSize":80, "exclusiveSize":652, "length":1, "capacity":128, + "members":[ + {"name":"[]", "staticSize":4, "exclusiveSize":0, + "members":[ + {"name":"a", "staticSize":4, "exclusiveSize":4} + ]} + ]}, + {"name":"v2", "size":656, "staticSize":80, "exclusiveSize":648, "length":2, "capacity":128, + "members":[ + {"name":"[]", "staticSize":4, "exclusiveSize":0, + "members":[ + {"name":"b", "staticSize":4, "exclusiveSize":4} + ]}, + {"name":"[]", "staticSize":4, "exclusiveSize":0, + "members":[ + {"name":"b", "staticSize":4, "exclusiveSize":4} + ]} + ]} + ]}]''' diff --git a/types/deque_list_type.toml b/types/deque_list_type.toml index a59742a..9227472 100644 --- a/types/deque_list_type.toml +++ b/types/deque_list_type.toml @@ -33,3 +33,95 @@ void getSizeType(const %1% &container, size_t& returnArg) } """ +traversal_func = """ +#ifdef __GLIBCXX__ +/* The std::deque container is a sequence of blocks, each of which is a contiguous + * array of elements. The adresses of the blocks are stored within their own array + * of pointers and is called the `map`. + * So the formula to compute the exclusive size of a std::deque is: + * `static size` + `total map size` + `total block size` - `total element size` + * `static size` = sizeof(std::deque) + * `total map size` = `map capacity` * `pointer size` + * `total block size` = `block count` * `block size` + * `total element size` = `element count` * `element size` + * Also see: https://godbolt.org/z/4znaz4hcd + * + * We don't have access to the map_capacity of the std::deque container, so we manually + * re-create the layout of the container to access it. + * + * https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a00686_source.html#l00509 + * From the source of , a std::deque has the following members: + */ +struct OI_Deque_impl_data { + std::nullptr_t map; + size_t map_capacity; + typename container_type::const_iterator start; + typename container_type::const_iterator finish; +}; + +const auto *rc = reinterpret_cast(&container); +const auto map_capacity = rc->map_capacity; + +const auto begin = container.begin(); +const auto end = container.end(); +const auto block_count = 1 + std::distance(begin._M_node, end._M_node); +const auto block_size = (uintptr_t)begin._M_last - (uintptr_t)begin._M_first; + +constexpr auto static_size = sizeof(container_type); +const auto total_map_size = map_capacity * sizeof(std::nullptr_t); +const auto total_block_size = block_count * block_size; +const auto total_element_size = container.size() * sizeof(typename container_type::value_type); + +const auto exclusive_size = static_size + total_map_size + total_block_size - total_element_size; +const auto capacity = total_block_size / sizeof(typename container_type::value_type); +#elif _LIBCPP_VERSION +static_assert(false, "libc++ is currently not supported"); +#else +static_assert(false, "Unsupported STL container. Seet types/deque_list_type.toml"); +#endif + +auto tail = returnArg + .write((uintptr_t)&container) + .write(exclusive_size) + .write(capacity) + .write(container.size()); + +for (auto&& it: container) { + tail = tail.delegate([&ctx, &it](auto ret) { + return OIInternal::getSizeType(ctx, it, ret); + }); +} + +return tail.finish(); +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +el.pointer = std::get(d.val).value; +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +el.exclusive_size = std::get(d.val).value; +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +el.container_stats.emplace(result::Element::ContainerStats { + .capacity = std::get(d.val).value, +}); +""" + +[[codegen.processor]] +type = "types::st::List::type>" +func = """ +static constexpr auto childField = make_field("[]"); + +auto list = std::get(d.val); +el.container_stats->length = list.length; + +stack_ins(inst::Repeat{ list.length, childField }); +"""