Skip to content

Constant vtables for object-literal methods, skipping the per-cast heap patch#254

Merged
ASDAlexander77 merged 1 commit into
mainfrom
interface-vtable-simplify-pr2
Jul 19, 2026
Merged

Constant vtables for object-literal methods, skipping the per-cast heap patch#254
ASDAlexander77 merged 1 commit into
mainfrom
interface-vtable-simplify-pr2

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Summary

  • §3 of docs/interface-vtable-simplification-design.md (follow-up to Heap-allocate patched interface vtable so it survives in a global __cctor #251/Fix cross-module interface field/method vtable index mismatch #252): an object-literal method's field value is a compile-time-known function symbol (FlatSymbolRefAttr) regardless of whether it captures variables - captures land in a separate .captured data field the function reads via this, not a different function per instance.
  • New side table objectLiteralMethodSymbolsMap (keyed on the object literal's own ObjectStorageType, populated in addObjectFuncFieldInfo) lets mlirGenObjectVirtualTableDefinitionForInterface emit a constant SymbolRefOp for a method slot instead of the GEP-on-null offset placeholder that previously required mlirGenCreateInterfaceVTableForObject to heap-clone and runtime-patch the vtable on every cast. When every interface method resolves this way, the clone+patch is skipped entirely - same footing as a method-less interface. The runtime patch remains as a fallback for methods whose funcName isn't known locally (an object type reconstructed from a @dllimport declaration).
  • Verified the vtable-sharing premise doesn't collide two differently-implemented same-shape literals on one constant global: vtable globals are named structurally (hash_value(objectType)), but a method field's FunctionType embeds the literal's own ObjectStorageType symbol (source location) as its this parameter, so "same shape" never actually holds once the method's own signature is counted - confirmed against a compiled counterexample before relying on it.
  • Fixed a bug found while implementing: the gate for the substitution has to be the interface's own method/field categorization (InterfaceInfo::methods), not merely "is this vtable slot func-typed". interface Something { toString: () => string; } categorizes toString as an interface field (PropertySignature with a function type), not a method - the access site (InterfaceFieldAccess, offset semantics) is different from a real MethodSignature's (InterfaceMethodAccess, raw function-pointer slot). The first version of this change substituted a constant symbol into any func-typed slot, handing a raw pointer to code that computes thisVal + slotValue expecting an offset - a garbage call target, crash confirmed via a WinDbg dump (call qword ptr [rax+rdx], both registers built from the garbage sum).

Test plan

  • New regression test 00interface_function_typed_field.ts (the PropertySignature-with-function-type vs MethodSignature distinction), both AOT and JIT
  • --emit=mlir spot checks: real methods (00interface_global_method.ts) now bake a ts.SymbolRef into the vtable global with no heap ts.New for the vtable clone; function-typed fields (00interface_object.ts) still correctly use the GEP-on-null offset scheme
  • Full suite: ctest -C Debug - 718/718 passed, no regressions

🤖 Generated with Claude Code

@ASDAlexander77
ASDAlexander77 merged commit 1db740c into main Jul 19, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the interface-vtable-simplify-pr2 branch July 19, 2026 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant