diff --git a/include/yorel/yomm2/detail.hpp b/include/yorel/yomm2/detail.hpp index 391c5846..d3c795aa 100644 --- a/include/yorel/yomm2/detail.hpp +++ b/include/yorel/yomm2/detail.hpp @@ -986,7 +986,7 @@ void decode_dispatch_data(Data& init) { // definitions are in reverse order, because of how 'chain' works. While // building the array of array of defintions, we put them back in the order // in which the compiler saw them. - auto packed_slots_iter = init.packed.slots; + auto packed_slots_iter = init.encoded.slots; auto methods = (method_info**)alloca(method_count * pointer_size); auto methods_iter = methods; auto method_defs = (uintptr_t**)alloca(method_count * pointer_size); @@ -996,85 +996,96 @@ void decode_dispatch_data(Data& init) { auto multi_method_to_method = (std::size_t*)alloca(multi_method_count * sizeof(std::size_t)); auto multi_method_to_method_iter = multi_method_to_method; - auto method_index = 0; - for (auto& method : Policy::methods) { - ++trace << "method " << method.name << "\n"; - indent _(trace); + { + auto method_index = 0; - *methods_iter++ = &method; + for (auto& method : Policy::methods) { + ++trace << "method " << method.name << "\n"; + indent _(trace); - ++trace << "specializations:\n"; + *methods_iter++ = &method; - for (auto& spec : method.specs) { - indent _(trace); - ++trace << spec.pf << " " << type_name(spec.type) << "\n"; - } + ++trace << "specializations:\n"; - auto slots_strides_count = 2 * method.arity() - 1; - - // copy slots and strides into the method's static - ++trace << "installing " << slots_strides_count - << " slots and strides\n"; - std::copy_n( - packed_slots_iter, slots_strides_count, method.slots_strides_ptr); - packed_slots_iter += slots_strides_count; - - auto specs = - (uintptr_t*)alloca((method.specs.size() + 2) * pointer_size); - *method_defs_iter++ = specs; - ++trace << "specs index: " << specs << "\n"; - specs = std::transform( - method.specs.begin(), method.specs.end(), specs, - [](auto& spec) { return (uintptr_t)spec.pf; }); - *specs++ = (uintptr_t)method.ambiguous; - *specs++ = (uintptr_t)method.not_implemented; + for (auto& spec : method.specs) { + indent _(trace); + ++trace << spec.pf << " " << type_name(spec.type) << "\n"; + } - if (method.arity() >= 2) { - ++trace << "m-method " - << (multi_method_to_method_iter - multi_method_to_method) - << " is method " << method_index << "\n"; - *multi_method_to_method_iter++ = method_index; + auto slots_strides_count = 2 * method.arity() - 1; + + // copy slots and strides into the method's static + ++trace << "installing " << slots_strides_count + << " slots and strides\n"; + std::copy_n( + packed_slots_iter, slots_strides_count, + method.slots_strides_ptr); + packed_slots_iter += slots_strides_count; + + auto specs = + (uintptr_t*)alloca((method.specs.size() + 2) * pointer_size); + *method_defs_iter++ = specs; + ++trace << "specs index: " << specs << "\n"; + specs = std::transform( + method.specs.begin(), method.specs.end(), specs, + [](auto& spec) { return (uintptr_t)spec.pf; }); + *specs++ = (uintptr_t)method.ambiguous; + *specs++ = (uintptr_t)method.not_implemented; + ++method_index; } - - ++method_index; } - // Build a table of pointers to dispatch tables, for multi-methods only. - - // decode the dispatch tables for multi-methods, and keep track of them in - // an array. We will use it when we fill the vtables. The packed dispatch - // tables are in compiler order. - - auto packed_iter = init.packed.dispatch; - auto decode_iter = init.decode; + // Decode dispatch tables for multi-methods, in place, and keep track of + // them in an array. We will use it when we fill the vtables. ++trace << "decoding multi-method dispatch tables\n"; + { + std::size_t method_index = 0; + auto dtbl_iter = init.dtbls; + + for (auto& method : Policy::methods) { + // Resist the temptation to use 'continue' to skip uni-methods, as + // 'method_index' needs to be incremented. + if (method.arity() > 1) { + indent _(trace); - for (auto i = 0; i < multi_method_count; ++i) { - assert((char*)decode_iter < (char*)packed_iter); - indent _(trace); + dispatch_tables[method_index] = dtbl_iter; + ++trace << "multi-method " << method_index + << " dispatch table at " << dtbl_iter << "\n"; - dispatch_tables[i] = decode_iter; - ++trace << "multi-method " << i << " dispatch table at " << decode_iter - << "\n"; + indent __(trace); + ++trace << "specs:"; - indent __(trace); - ++trace << "specs:"; + auto defs = method_defs[method_index]; + bool more = true; - auto defs = method_defs[multi_method_to_method[i]]; + while (more) { + more = !(*dtbl_iter & stop_bit); + auto spec_index = *dtbl_iter & ~stop_bit; + trace << " " << spec_index; + *dtbl_iter = defs[spec_index]; + }; - do { - auto spec_index = *packed_iter & ~stop_bit; - trace << " " << spec_index; - *decode_iter++ = defs[spec_index]; - } while (!(*packed_iter++ & stop_bit)); + trace << "\n"; + } - trace << "\n"; + ++method_index; + } } ++trace << "decoding v-tables\n"; - auto vtbl_iter = init.packed.vtbl; + + auto encode_iter = init.encoded.vtbls; + auto decode_iter = init.vtbls; + + auto emit = [&](std::uintptr_t value) { + assert((char*)(decode_iter + 1) <= (char*)encode_iter); + assert( + (char*)(decode_iter + 1) <= + (char*)(decode_iter + sizeof(init.dtbls) / sizeof(*init.dtbls))); + *decode_iter++ = value; + }; for (auto& cls : Policy::classes) { if (*cls.static_vptr != nullptr) { @@ -1086,42 +1097,49 @@ void decode_dispatch_data(Data& init) { indent _2(trace); - auto first_slot = *vtbl_iter++; + auto first_slot = *encode_iter++; ++trace << "first slot: " << first_slot << "\n"; *cls.static_vptr = decode_iter - first_slot; do { - auto entry = *vtbl_iter & ~stop_bit; + auto entry = *encode_iter & ~stop_bit; if (entry & index_bit) { - auto index = *vtbl_iter & ~index_bit; + auto index = *encode_iter & ~index_bit; ++trace << "multi-method group " << index << "\n"; - *decode_iter++ = index; + emit(index); } else { auto method_index = entry; auto method = methods[method_index]; - auto group_index = *++vtbl_iter & ~stop_bit; // spec or group + auto group_index = *++encode_iter & ~stop_bit; // spec or group if (method->arity() == 1) { ++trace << "uni-method " << method_index << " spec " << group_index; - *decode_iter++ = method_defs[method_index][group_index]; + emit(method_defs[method_index][group_index]); } else { ++trace << "multi-method " << method_index << " group " << group_index; indent _(trace); trace << type_name(method->method_type) << "\n"; - *decode_iter++ = - (std::uintptr_t)(dispatch_tables[method_index] + - group_index); + emit((std::uintptr_t)(dispatch_tables[method_index] + + group_index)); } trace << "\n"; indent _(trace); ++trace << type_name(method->method_type) << "\n"; } - } while (!(*vtbl_iter++ & stop_bit)); + } while (!(*encode_iter++ & stop_bit)); + } + + ++trace << decode_iter << " " << encode_iter << "\n"; + + auto waste = sizeof(init.encoded) - sizeof(init.vtbls); + + if (waste > 0) { + ++trace << waste << " bytes wasted\n"; } using namespace policy; diff --git a/include/yorel/yomm2/generator.hpp b/include/yorel/yomm2/generator.hpp index d011bbc9..0fffd321 100644 --- a/include/yorel/yomm2/generator.hpp +++ b/include/yorel/yomm2/generator.hpp @@ -295,7 +295,11 @@ void generator::encode_dispatch_data( auto dispatch_tables_size = std::accumulate( compiler.methods.begin(), compiler.methods.end(), size_t(0), [](auto sum, auto& method) { - return sum + method.dispatch_table.size(); + if (method.arity() == 1) { + return sum; + } else { + return sum + method.dispatch_table.size(); + } }); size_t encode_vtbl_size = 0, decode_vtbl_size = 0; @@ -324,28 +328,43 @@ void generator::encode_dispatch_data( // Write data structure declaration and open initializer. char prelude_format[] = R"( - static union { - struct { - uint16_t headroom[%d]; - uint16_t slots[%d]; - uint16_t dispatch[%d]; - uint16_t vtbl[%d]; - } packed; - std::uintptr_t decode[%d]; - } yomm2_dispatch_data = { { {}, { + static struct { + union { + struct { + uint16_t headroom[%d]; + uint16_t slots[%d]; + uint16_t vtbls[%d]; + } encoded; + std::uintptr_t vtbls[%d]; + }; + std::uintptr_t dtbls[%d]; + } yomm2_dispatch_data = { { { {}, { )"; + const auto decode_bytes = sizeof(uintptr_t); + const auto encode_bytes = sizeof(uint16_t); + const auto total_decode_size = dispatch_tables_size + decode_vtbl_size; const auto total_encode_size = slots_and_strides_size + dispatch_tables_size + encode_vtbl_size; - const auto headroom = total_decode_size * sizeof(uintptr_t) - + auto headroom = total_decode_size * sizeof(uintptr_t) - total_encode_size * sizeof(uint16_t); + + const auto dispatch_and_vtbl_bytes = + (dispatch_tables_size + encode_vtbl_size) * encode_bytes; + const auto total_decode_bytes = total_decode_size * decode_bytes; + + if (dispatch_and_vtbl_bytes < total_decode_bytes) { + headroom += + (total_decode_bytes - dispatch_and_vtbl_bytes) / encode_bytes; + } + char prelude[sizeof(prelude_format) + 5 * 6]; std::snprintf( prelude, sizeof(prelude), prelude_format, headroom / sizeof(uint16_t), - slots_and_strides_size, dispatch_tables_size, encode_vtbl_size, - total_decode_size); + slots_and_strides_size, encode_vtbl_size, decode_vtbl_size, + dispatch_tables_size); os << prelude; std::vector methods; @@ -378,37 +397,12 @@ void generator::encode_dispatch_data( os << " }, {\n"; - // ------------------------------------------------------------------------- - // Write multi-methods dispatch tables. - - os << indent << "// multi-methods dispatch tables\n"; - - for (auto& method : - range(compiler.methods.begin(), compiler.methods.end())) { - if (method.arity() < 2) { - continue; - } - - os << indent << "// " << boost::core::demangle(method.info->name.data()) - << "\n"; - os << indent; - auto dt_iter = std::transform( - method.dispatch_table.begin(), method.dispatch_table.end() - 1, - std::ostream_iterator(os, ", "), - [&method](auto entry) { return uint16_t(entry->spec_index); }); - auto& last = method.dispatch_table.back(); - *dt_iter = (uint16_t)last->spec_index | stop_bit; - os << "\n"; - } - - os << " }, {\n"; - // ------------------------------------------------------------------------- // Write v-tables. os << indent << "// v-tables\n"; - for (auto& cls : range(compiler.classes.begin(), compiler.classes.end())) { + for (auto& cls : compiler.classes) { os << indent << "// " << boost::core::demangle( reinterpret_cast(cls.type_ids[0]) @@ -449,10 +443,35 @@ void generator::encode_dispatch_data( } } + os << " } } }, {\n"; + + // ------------------------------------------------------------------------- + // Write multi-methods dispatch tables. + + os << indent << "// multi-methods dispatch tables\n"; + + for (auto& method : + range(compiler.methods.begin(), compiler.methods.end())) { + if (method.arity() < 2) { + continue; + } + + os << indent << "// " << boost::core::demangle(method.info->name.data()) + << "\n"; + os << indent; + auto dt_iter = std::transform( + method.dispatch_table.begin(), method.dispatch_table.end() - 1, + std::ostream_iterator(os, ", "), + [&method](auto entry) { return uint16_t(entry->spec_index); }); + auto& last = method.dispatch_table.back(); + *dt_iter = (uint16_t)last->spec_index | stop_bit; + os << "\n"; + } + // ------------------------------------------------------------------------- // Close braces. - os << " } } };\n\n"; + os << " } };\n\n"; // ------------------------------------------------------------------------- // Write call to decoder.