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.
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:
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 ofunsafe { … }blocks; LLVM cost is ~linear in IR size — both shrink dramatically. The "via cast" variant also subsumes thelet 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/— primarilymethod.rs/interface.rs, plus the correspondingcpp_*.rsfor 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:-> Result<()>)BOOL/HANDLEdirectly (noand_then)*mut u32, *mut *mut T) usingArray::from_raw_partsOption<&T>/InParamparameters by-ref vs. by-valueunsafe extern "system" fnthunk side — the_Implshim bodies have a mirror-image of the same problemWorth 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.rsbyte size and a measurable drop inLLVM_passesandMIR_borrow_checkingon a clean dev build, measured withcargo +nightly rustc -- -Ztime-passes.4. No public-API change
Generated
pub fnsignatures inbindings.rsmust 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_Implhalf. #4377 attacks the non-generic call-site half that dominates LLVM time.6. One non-obvious gotcha
windows_core::Interface::vtable(self)andas_raw(self)are taken twice in many emitted bodies (once forself, once for eachparam().abi()that needs an interface). The helper signature should takeself: &Ionce and re-derive both internally — otherwise inlining will be the only thing rescuing call sites with 4–5 interface params.