◐ Off-By-One · answer catalog

jit-inline-cache-polymorphism-20260728

1 answer(s)jsnode20

jit-inline-cache-polymorphism-20260728

📦 Source in repository (JSON)

Answer

The implementation provides a complete Polymorphic Inline Cache (PIC) optimization pass for a JavaScript JIT compiler. The core design mirrors V8's IC system:

Shape System — Each object carries a Shape (hidden class) that tracks property offsets and transitions. When a property is added, the shape transitions to a new child shape, mirroring how V8's Map objects work.

Inline Cache States — Three dispatch tiers based on observed object shapes at a call site: 1. Monomorphic (1 shape): Single shape-guard → direct offset load 2. Polymorphic (2–4 shapes): Cascading shape-ID checks via a move-to-front heuristic 3. Megamorphic (5+ shapes): Dictionary-style slow path using a shape-ID → value cache

Stub Generation — Each IC emits a structured "stub" object containing guards (shape_check), load type (direct_load or prototype_chain_load), and a fallback handler. A real JIT backend lowers these stubs to native code (x86/ARM).

Prototype Chain Lookup — The lookupOffset method on Shape walks the prototype chain of shapes. The runtime doPrototypeChain helper walks parallel JSObject prototype objects with visited-set cycle detection.

Key code from the solution:

// Shape (hidden class) with property map, prototype link, and transition table
class Shape {
  constructor(prototype = null) {
    this.id = ++shapeIdCounter;
    this.properties = new Map();      // name → { offset, writable, ... }
    this.prototype = prototype;       // parent shape in prototype chain
    this.transitions = new Map();     // property name → { shape, offsetName }
  }

  lookupOffset(name) {
    if (this.properties.has(name)) return this.properties.get(name);
    if (this.prototype) return this.prototype.lookupOffset(name);
    return null;
  }
}

// Inline cache with three dispatch states
class InlineCache {
  constructor(name) {
    this.state = IC_STATE.UNINITIALIZED;
    this.monomorphicShape = null;
    this.polymorphicShapes = [];       // up to 4 shapes
    this.megamorphicCache = new Map(); // shapeId → cached value
  }

  recordAccess(object, propertyName) {
    const shape = object.shape;
    switch (this.state) {
      case IC_STATE.UNINITIALIZED:
        this.monomorphicShape = shape;
        this.state = IC_STATE.MONOMORPHIC;
        break;
      case IC_STATE.MONOMORPHIC:
        if (!this.monomorphicShape.matches(shape)) {
          // Second shape → polymorphic
          this.polymorphicShapes = [this.monomorphicShape, shape];
          this.monomorphicShape = null;
          this.state = IC_STATE.POLYMORPHIC;
        }
        break;
      case IC_STATE.POLYMORPHIC:
        // ... checks existing, adds up to 4, then megamorphic
      case IC_STATE.MEGAMORPHIC:
        this.megamorphicCache.set(shape.id, null);
    }
  }

  generateStub(propertyName) {
    // Returns structured IR: guards[], fallback type
  }
}

Evidence & signatures

Verified with 12 comprehensive test suites covering all states, transitions, and edge cases:

| Test | Scenario | Result |
|------|----------|--------|
| 1 | Monomorphic IC — single shape, 10+ accesses stays mono | ✓ hitCount=11 |
| 2 | Polymorphic (2 shapes) — two hidden classes | ✓ poly state, 2 cached |
| 3 | Polymorphic (4 shapes) — max polymorphic capacity | ✓ 4 shapes, re-read correct |
| 4 | Megamorphic (6 shapes) — overflow to dictionary | ✓ all values correct |
| 5 | Hidden class transitions — adding properties changes shape | ✓ original + new accessible |
| 6 | Prototype chain lookups — own + inherited + missing | ✓ y=99, x=10, undefined |
| 7 | Stub generation — guards, shape IDs, execution | ✓ monomorphic stub returns 77 |
| 8 | JIT integration + V8-style benchmark (1000 shapes) | ✓ megamorphic, sum=15 |
| 9 | Move-to-front heuristic — hot shape promoted | ✓ shape[2] at front |
| 10 | Megamorphic dictionary — 8 shapes, all correct | ✓ size=8, values correct |
| 11 | Shape identity — structurally identical ≠ same shape | ✓ matches works correctly |
| 12 | Prototype chain + shape transition — combined | ✓ prototype survives transition |

**Edge cases handled:**
- `__proto__` name collision avoided (uses `protoObj` property)
- `hasOwnProperty` called safely via `Object.prototype.hasOwnProperty.call`
- Cycle detection in prototype chain walks
- Miss handler fallback when no guard matches

---
{"model": "claude-sonnet-4-20250514", "problem_class": "jit-inline-cache-polymorphism-20260728", "result": "passed", "tests": 12}
Generated from the verified corpus · MIT licensedBack to the catalog