Skip to content

Bug: implements_delegate non-fold-ability #1334

@jonwis

Description

@jonwis

Version

2.0.220131.2

Summary

Using SizeBench, some reasonable fraction of our DLL is taken up in winrt::impl::implements_delegate methods that aren't foldable for one of two reasons:

  • The offset of the reference count member shifts around from +10 to +18 depending on the complexity of the delegate
  • The "which guid are you?" checks end up generating relative lea ... operations.

Reproducible example

No response

Expected behavior

It may be a linker/optimizer bug that the folder is not working "hard enough."

Actual behavior

Multiple nonfolded specializations of delegate types taking ~1% of binary space (~15k out of ~1500k).

Additional comments

In other places, I've used a "base" type to move as many things down to non-templated members as possible, with a templated caller sending in a parameters. For instance, in base.h, this appears to work and collapse all those methods into a small handful of specialized "push some parameters and call a helper" method.

Things that could use a similar treatment:

  • winrt::event::add - make_agile_delegate's use of guid_of
  • root_implements - find_interface use of guids
  • await_adapter::await_suspend - looks like vtable handling

In my 1.5mb DLL, SizeBench claims those three waste another 1%.

namespace winrt::impl
{
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4458) // declaration hides class member (okay because we do not use named members of base class)
#endif

    struct implements_delegate_base
    {
        WINRT_IMPL_NOINLINE uint32_t __stdcall AddRef() noexcept
        {
            return ++m_references;
        }

        WINRT_IMPL_NOINLINE uint32_t __stdcall Release() noexcept
        {
            return --m_references;
        }

        WINRT_IMPL_NOINLINE uint32_t QueryInterfaceCommon(guid const& id, void** result, unknown_abi* abi_t_ptr, guid const& delegate_id) noexcept
        {
            if ((delegate_id == id) || is_guid_of<Windows::Foundation::IUnknown>(id) || is_guid_of<IAgileObject>(id))
            {
                *result = abi_t_ptr;
                AddRef();
                return 0;
            }

            if (is_guid_of<IMarshal>(id))
            {
                return make_marshaler(abi_t_ptr, result);
            }

            *result = nullptr;
            return error_no_interface;
        }

    public:
        atomic_ref_count m_references{ 1 };
    };

    template <typename T, typename H>
    struct implements_delegate : implements_delegate_base, abi_t<T>, H, update_module_lock
    {
        implements_delegate(H&& handler) : H(std::forward<H>(handler))
        {
        }

        int32_t __stdcall QueryInterface(guid const& id, void** result) noexcept final { return implements_delegate_base::QueryInterfaceCommon(id, result, static_cast<abi_t<T>*>(this), guid_of<T>()); }

        uint32_t __stdcall AddRef() noexcept final { return implements_delegate_base::AddRef(); }

        uint32_t __stdcall Release() noexcept final
        {
            auto remaining = implements_delegate_base::Release();
            if (remaining == 0)
            {
                delete static_cast<delegate<T, H>*>(this);
            }
            return remaining;
        }
    };

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions