From 331c47705c722b35739758918f1270aa1ade76a4 Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Thu, 7 Sep 2023 13:12:54 -0700 Subject: [PATCH] Implement Container V2 for folly::small_vector --- test/integration/folly_small_vector.toml | 25 +++++++--- types/small_vec_type.toml | 61 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/test/integration/folly_small_vector.toml b/test/integration/folly_small_vector.toml index 360c323..5ae53f3 100644 --- a/test/integration/folly_small_vector.toml +++ b/test/integration/folly_small_vector.toml @@ -1,28 +1,27 @@ includes = ["folly/small_vector.h", "vector"] [cases] [cases.int_default_empty] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321 param_types = ["const folly::small_vector&"] setup = "return {};" expect_json = '[{"staticSize":16, "dynamicSize":0, "exclusiveSize":16, "length":0, "capacity":2, "elementStaticSize":4}]' + expect_json_v2 = '[{"staticSize":16, "exclusiveSize":16, "length":0, "capacity":2}]' [cases.int_default_inlined] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321 param_types = ["const folly::small_vector&"] setup = "return {{1,2}};" expect_json = '[{"staticSize":16, "dynamicSize":0, "exclusiveSize":16, "length":2, "capacity":2, "elementStaticSize":4}]' + expect_json_v2 = '[{"staticSize":16, "exclusiveSize":8, "length":2, "capacity":2}]' [cases.int_default_overflow] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321 param_types = ["const folly::small_vector&"] setup = "return {{1,2,3,4}};" expect_json = '[{"staticSize":16, "dynamicSize":24, "exclusiveSize":40, "length":4, "capacity":6, "elementStaticSize":4}]' + expect_json_v2 = '[{"staticSize":16, "exclusiveSize":24, "length":4, "capacity":6}]' [cases.vector_3_empty] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321 param_types = ["const folly::small_vector, 3>&"] setup = "return {};" expect_json = '[{"staticSize":80, "dynamicSize":0, "exclusiveSize":80, "length":0, "capacity":3, "elementStaticSize":24}]' + expect_json_v2 = '[{"staticSize":80, "exclusiveSize":80, "length":0, "capacity":3}]' [cases.vector_3_inlined] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321 param_types = ["const folly::small_vector, 3>&"] setup = "return {{ {1,2,3}, {4}, {5,6} }};" expect_json = '''[ @@ -31,8 +30,13 @@ includes = ["folly/small_vector.h", "vector"] {"staticSize":24, "dynamicSize":4, "exclusiveSize":28, "length":1, "capacity":1, "elementStaticSize":4}, {"staticSize":24, "dynamicSize":8, "exclusiveSize":32, "length":2, "capacity":2, "elementStaticSize":4} ]}]''' + expect_json_v2 = '''[ + {"staticSize":80, "length":3, "exclusiveSize":8, "capacity":3, "members":[ + {"staticSize":24, "exclusiveSize":24, "length":3, "capacity":3}, + {"staticSize":24, "exclusiveSize":24, "length":1, "capacity":1}, + {"staticSize":24, "exclusiveSize":24, "length":2, "capacity":2} + ]}]''' [cases.vector_3_overflow] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321 param_types = ["const folly::small_vector, 3>&"] setup = "return {{ {1,2,3}, {4}, {5,6}, {7} }};" expect_json = '''[ @@ -42,9 +46,16 @@ includes = ["folly/small_vector.h", "vector"] {"staticSize":24, "dynamicSize":8, "exclusiveSize":32, "length":2, "capacity":2, "elementStaticSize":4}, {"staticSize":24, "dynamicSize":4, "exclusiveSize":28, "length":1, "capacity":1, "elementStaticSize":4} ]}]''' + expect_json_v2 = '''[ + {"staticSize":80, "exclusiveSize":104, "length":4, "capacity":5, "members":[ + {"staticSize":24, "exclusiveSize":24, "length":3, "capacity":3}, + {"staticSize":24, "exclusiveSize":24, "length":1, "capacity":1}, + {"staticSize":24, "exclusiveSize":24, "length":2, "capacity":2}, + {"staticSize":24, "exclusiveSize":24, "length":1, "capacity":1} + ]}]''' [cases.int_always_heap] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321 param_types = ["const folly::small_vector&"] setup = "return {{1}};" expect_json = '[{"staticSize":16, "dynamicSize":4, "exclusiveSize":20, "length":1, "capacity":1, "elementStaticSize":4}]' + expect_json_v2 = '[{"staticSize":16, "exclusiveSize":16, "length":1, "capacity":1}]' diff --git a/types/small_vec_type.toml b/types/small_vec_type.toml index 20a0288..4014c10 100644 --- a/types/small_vec_type.toml +++ b/types/small_vec_type.toml @@ -41,6 +41,7 @@ void getSizeType(const %1% &container, size_t& returnArg) } } """ + handler = """ template struct TypeHandler> { @@ -67,3 +68,63 @@ struct TypeHandler> { } }; """ + +traversal_func = """ +// If `container.data()` pointer is within the container struct, +// then the container's storage is inlined and doesn't uses the heap. +// TODO: Is there an API to get this information? +bool uses_intern_storage = + (uintptr_t)&container <= (uintptr_t)container.data() && + (uintptr_t)container.data() < ((uintptr_t)&container + sizeof(container)); + +auto tail = returnArg + .write((uintptr_t)uses_intern_storage) + .write(container.capacity()) + .write(container.size()); + +for (auto &&it: container) { + tail = tail.delegate([&it](typename TypeHandler::type ret) { + return OIInternal::getSizeType(it, ret); + }); +} + +return tail.finish(); +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +// Using the container's pointer to temporarily store the uses_intern_storage boolean. +// TODO: Is there another way to pass a value across processors? +el.pointer = 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 = """ +// Reading the `uses_intern_storage` boolean that was stored in `pointer` by the processor above. +bool uses_intern_storage = std::exchange(el.pointer.value(), (uintptr_t)0); + +auto list = std::get(d.val); +el.container_stats->length = list.length; + +if (uses_intern_storage) { + // The storage is inlined, so don't double count for items using the intern storage. + el.exclusive_size -= list.length * sizeof(T0); +} else { + // The storage is heap allocated, so add any unused capacity. + el.exclusive_size += (el.container_stats->capacity - el.container_stats->length) * sizeof(T0); +} + +static constexpr auto childField = make_field("[]"); +for (size_t i = 0; i < list.length; i++) + ins.emplace(childField); +"""