Skip to content

windows-bindgen collapse per-method call sites #4377

@kennykerr

Description

@kennykerr

Collapse the per-method call-site body the same way (the other big LLVM win). Every one of the pub fn wrappers in bindings.rs expands to roughly 10 lines:

unsafe {
    let mut result__ = core::mem::zeroed();
    (windows_core::Interface::vtable(self).M)(
        windows_core::Interface::as_raw(self), arg.param().abi(), &mut result__,
    ).and_then(|| windows_core::Type::from_abi(result__))
}

That repetitive shape is what makes LLVM_passes 15 s and MIR_borrow_checking 4.6 s. Encapsulate the shape into a small set of generic helpers in windows_core::imp (e.g. call_in_out, call_in, call_out_via, call_via_cast) so each generated method body becomes one tail call. Borrow-check is roughly linear in the number of unsafe { … } blocks; LLVM cost is ~linear in IR size — both shrink dramatically. The "via cast" variant also subsumes the let this = &Interface::cast::<IFooN>(self)?; prelude that appears in hundreds of places.

More notes to consider:

1. Where the bodies are emitted

The call-site bodies are generated in crates/libs/bindgen/src/types/ — primarily method.rs / interface.rs, plus the corresponding cpp_*.rs for COM/Win32. Naming those up front saves a search pass.

2. Variant coverage

The four helpers listed (call_in_out, call_in, call_out_via, call_via_cast) cover the common shapes, but the emitter today also handles:

  • HRESULT-only methods with no out param (-> Result<()>)
  • Methods returning a non-HRESULT scalar / BOOL / HANDLE directly (no and_then)
  • Out-array shapes (*mut u32, *mut *mut T) using Array::from_raw_parts
  • Option<&T> / InParam parameters by-ref vs. by-value
  • The unsafe extern "system" fn thunk side — the _Impl shim bodies have a mirror-image of the same problem

Worth stating explicitly: if a shape doesn't fit one of the helpers, fall back to the current inline expansion rather than growing the helper matrix unboundedly. Otherwise the agent will try to make one helper handle everything and the generic bounds get gnarly.

3. Success criteria

Concrete numbers help. Suggested target: expect a further ≥20% drop in bindings.rs byte size and a measurable drop in LLVM_passes and MIR_borrow_checking on a clean dev build, measured with cargo +nightly rustc -- -Ztime-passes.

4. No public-API change

Generated pub fn signatures in bindings.rs must be byte-identical; only the body shape changes. Easy to fixture-test (crates/tests/fixtures/harness/) and prevents the agent from "helpfully" simplifying signatures.

5. Complementary to #4375

Worth framing: the wins compound. #4375 (--implements) removes the mostly-uninstantiated generic _Impl half. #4377 attacks the non-generic call-site half that dominates LLVM time.

6. One non-obvious gotcha

windows_core::Interface::vtable(self) and as_raw(self) are taken twice in many emitted bodies (once for self, once for each param().abi() that needs an interface). The helper signature should take self: &I once and re-derive both internally — otherwise inlining will be the only thing rescuing call sites with 4–5 interface params.

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions