
# Bayt Kodu VM'si

Varsayılan yürütme yolu bir ağaç yürüyücüsüdür — her AST düğümünü ziyaret eder ve bir değer döndürür. Milyonlarca kez çağrılan sık kullanılan işlevlerde, düğüm başına dağıtım ek yükü darboğaz oluşturur. Phasis bu ek yükü azaltmak için üzerine spekülatif bir bayt kodundan PHP closure'ına derleyici katmanı ekler.

## VM ne zaman devreye girer

Her `JsFunction` için bir sık çağrı sayacı vardır. Sayaç eşiği (varsayılan 100) aşınca motor, `src/Bytecode/JsToPhp.php` aracılığıyla işlev gövdesini bir PHP closure'ına derlemeyi dener.

İşlev gövdesi bilinen bir şekil kümesine uyduğunda derleme başarılı olur:

- Öngörülebilir türlere (sayısal, nesne, işlev, dizi, dize) sahip yerel değişken atamaları.
- Sayılar üzerinde aritmetik işlemler.
- Nesne türündeki yerel değişkenlerde VE `this` üzerinde üye okumaları (`this.v`, `obj.x`).
- Nesne türündeki yerel değişkenlerde yöntem çağrıları (`obj.method(args)`); alıcı, `$thisValue` olarak iletilerek dağıtılır.
- `new Ctor(args)` kurucu çağrıları — LHS yerel değişkeninin türünü `object` olarak yükseltir ve nesne olmayan dönüşlere karşı Bailout koruması ekler.
- Basit koşul şekillerine sahip `for` / `while` döngüleri.
- Hesaplanabilir ifadeli `return`.
- Tür denetimli koşullar üzerinde `if` / `else`.

Gövde aşağıdakileri içerdiğinde derleme başarısız olur (ve işlev ağaç yürüyücüsünde kalır):

- `with` deyimleri.
- `try / catch / finally` (doğru tamamlama semantiği için ağaç yürüyücüsü tarafından işlenir).
- `eval()` çağrıları.
- Doğrudan `arguments` değişikliği.
- Derleyicinin henüz alt düzeye çevirmeyi bilmediği her şey.

Derleme başarılı olduğunda motor, işlevin çağrı yolunu derlenmiş closure ile değiştirir. Sonraki çağrılar, AST düğümü başına değil, işlev çağrısı başına tek bir ek yükle yerel PHP çalıştırır.

## Vazgeçmeler

Derlenmiş hızlı yol bazı varsayımlarda bulunur (sayılar sayı kalır, özellikler aynı şekilde kalır). Çalışma zamanında bir varsayım ihlal edilirse closure bir `Bailout` fırlatır ve o çağrının yürütmesi ağaç yürüyücüsünde yeniden başlar.

Belirtimin doğruluğu burada en çok önem taşır. Ağaç yürüyücüsü, başarısız hızlı yoldan hiçbir yan etki gözlemlemeden vazgeçilen çağrıyı baştan yürütebilmelidir. Phasis bunu iki şekilde sağlar:

1. **Saf önekler** — derleyici, spekülasyondan önce yan etkileri tarar. İşlev spekülasyon noktasından önce G/Ç (console.log, ana sistem işlevleri) yapıyorsa hızlı yol bunu atlamaz; işlemi tamamlar ve devam eder.
2. **Derleme zamanında reddetme** — gözlemlenebilir yan etkileri geri almayı gerektiren şekillerde (`fn()` durum değiştiren sayısal olmayan bir `let r = fn()` gibi), derleyici derlemeyi tamamen reddeder. İşlev ağaç yürüyücüsünde kalır.

Örnek durum: sayısal olmayan bir atama hızlı yolda `fn()` yürütür, `Bailout` fırlatır ve sonra ağaç yürüyücüsünde yeniden yürütülür — böylece `fn()` iki yan etki kümesiyle iki kez çalışır. Derleme zamanında reddetme, `fn()` işlevinin hızlı yolda hiç çalışmamasını sağlar; böylece ağaç yürüyücüsü bu çağrıyı yalnızca bir kez gözlemler.

## VM dağıtımı (ikinci katman)

Bazı işlevler PHP closure'larına derlenir; diğerleri `src/Bytecode/VM.php` içindeki daha basit bir bayt kodu yorumlayıcısına geçer. Bayt kodu, `switch` dağıtımının işlediği düz bir işlem kodu dizisidir (`LOAD_LOCAL`, `STORE_PROP`, `CALL_METHOD`, …).

VM; PHP closure'ına derlenmeye değmeyecek, ancak AST dolaşımını atlamaktan yarar sağlayacak kadar sık kullanılan işlevler içindir. Yerleşik nesnelerdeki yöntem çağrıları (Array.prototype.map, String.prototype.split, …), genel `callFunction` trampoline'ini atlayan VM hızlı yollarından geçer.

## Özel çağrı yığını

`Op::CALL`, `Op::CALL_METHOD` ve `Op::NEW_CALL`, JS çağrı yığınının tamamını `VM::execute` içinde tutan satır içi çağrı yolunu kullanır — her JS çağrısı için yeni bir PHP çerçevesi oluşturulmaz. Girişte, çağıranın durumu (pc, kod akışı, işlenen yığını, yerel değişkenler, env, `this`, katı mod bayrağı), VM başına `SavedFrame` örneklerinden oluşan bir `savedFrames` havuzuna aktarılır ve dağıtım döngüsü yerel değişkenlerini çağrılana göre yeniden düzenler. `Op::RET` kayıtlı çerçeveyi alıp durumu geri yükler. İstisnalar, ağaç yürüyücüsüne dönmeden önce kayıtlı çerçeveler boyunca açılır.

Uygunluk `JsFunction` üzerinde önbelleğe alınır: normal çağrılar için `vmInlineCallCache` (BC derlemeli gövde, `bind()` ile bağlanmamış, yerel değil), `new` ifadeleri için `vmInlineConstructCache` (ek olarak `isConstructable`, türetilmemiş; örnek alanı veya özel yöntem başlatıcısı yok). Diğer her şey standart `callFunction` / `vmNewExpression` trampoline'inden geçmeye devam eder.

Mimarinin ek bir yararı: PHP yığını sınırı artık JS özyineleme derinliğini kısıtlamaz. Artık tek sınır `CallStack::maxDepth` — **4 096** olarak ayarlanmıştır; tüm geçerli test262 durumlarının (çoğu 2 k çerçevenin altında kalır) çok üzerinde, patolojik sonsuz özyineleme testlerinin çalıştırıcı zaman aşımı bütçesi içinde hızla başarısız olacağı kadar düşüktür. Transpile edilmiş closure yolu (`JsToPhp`), satır içine alınmayan yedek yol için kendine ait 8 192 çerçevelik sınıra sahiptir.

## Çerçeve anlık görüntülü oluşturucular

Basit oluşturucu gövdeleri — `yield*` yok, try/catch/finally içinde `yield` yok, `await` yok — bayt koduna derlenir. Derleyici, gövdenin askıya alındığı yere yeni bir `Op::YIELD` işlem kodu ekler; `Op::YIELD` dağıtım durumunu (cf, pc+1, yığın, sp, yerel değişkenler, env, thisValue, strict) bir `GeneratorSnapshot` yığın nesnesine kaydeder ve `VM::execute` üzerinden bir `YieldResult` sentinel değeri döndürür. Sürücü `JsGenerator`, anlık görüntüyü saklar ve sonraki `next()` / `throw()` çağrısında `VM::execute` nesnesine geri iletir; işlenen yığınının tepesindeki devam değerinin yerini alır.

Bu, desteklenen alt kümede PHP-Fiber yolunun yerini alır. Fiber tabanlı sürücü (`executeGeneratorBody`), derleyicinin reddettiği şekiller için hâlâ kullanılır — `yield*` delegasyonu, try bloğu içinde yield yapan oluşturucular, asenkron oluşturucular, `await` içeren asenkron işlevler. `JsGenerator` çift kipli çalışır: her `next()` / `throw()` / `return()`, örnekte anlık görüntü mü yoksa Fiber mı bulunduğunu denetler ve uygun biçimde yönlendirir.

Tezgâhta ayarlanabilir bir düğme yoktur; uygunluk derleme zamanında belirlenir ve `JsFunction` üzerinde önbelleğe alınır. Kazanç, anlık görüntü yolundaki her yield için bir PHP bağlam geçişinin azalmasıdır; Fiber yolu diğer her şey için değişmeden kalır.

## Satır içi önbellekler

`Op::LOAD_MEMBER`, prototip kimliğine göre anahtarlanan ve her PC için kullanılan bir satır içi önbellek taşır; bu önbellek prototip yönteminin örnekte kullanıldığı örüntüde (`c.sum(...)`, `arr.push(...)`, tüm sınıf yöntemi dağıtımları) kullanılır. İsabet koşulu: alıcıda bu ad için kendine ait bir yuva bulunmaması, önbellekteki prototip başvurusunun alıcının `getPrototype()` değeriyle aynı olması ve prototipin `PropertyMap::$version` değerinin yakalanan sürümle hâlâ eşleşmesi.

Önbellek geçersizleştirme `PropertyMap::$version` üzerinden yürütülür. Yapısal değişiklikler (ayarlama / silme) bunu koşulsuz artırır. Veri yuvası değerlerinin üzerine yazma ise yalnızca `PropertyMap::$isUsedAsProto` true olduğunda artırır — nesne başka bir nesnenin `[[Prototype]]` değeri olarak ilk kez gözlemlendiğinde etkinleştirilen bir yazma bariyeri bayrağı. Asla prototip görevi görmeyen örnek nesneleri, her `obj.x = v` işleminde satır içi önbellek altyapısının maliyetini ödemez.

Getter erişimcileri hiçbir zaman önbelleğe alınmaz (her okumada yeniden çalışmaları gerekir).

`PropertyMap`, her yazmada *koşulsuz* artırılan ikinci bir sayaç olan `$mutationVersion` değerini taşır (yazma bariyeri yoktur). `JSON.stringify` önbelleği, ziyaret edilen her iç içe nesne/dizide bu sayacı sabitler; böylece `o.a = 99` gibi yalnızca örneği etkileyen bir değişiklik, satır içi önbelleğin `$version` artırmayı atlamış olabileceği durumlarda bile önbelleğe alınmış dizeyi doğru biçimde geçersiz kılar.

## JSON.stringify önbelleği

`JsObject`, `$jsonCacheString` + `$jsonCacheVersions` değerlerini taşır (önemsiz dizeleştirme sırasında ziyaret edilen her iç içe nesne için bir `[obj, mutationVersion]` çifti listesi). Yerine koyucu / boşluk karakteri olmadan yapılan sonraki `JSON.stringify(o)` çağrısında önemsiz hızlı yol, sabitlenen tüm sürümlerin değişmediğini doğrular ve yeniden dolaşıp kodlamadan önbelleğe alınmış dizeyi döndürür. `JSON.parse(JSON.stringify(o))` gibi sık yinelenen derin klonlama örüntüleri, dizeleştirme aşamasında yaklaşık 2× hızlanır.

Önbellek, eşleşme olmadığında (sabitlenen sürümlerden herhangi biri farklıysa) sessizce silinir ve sonraki dolaşımda yeniden doldurulur. `toJSON`, getter'lar, erişimciler, simgeler veya `[[PrimitiveValue]]` yuvaları bulunan nesneler belirtime uygun serileştiriciye geçer ve önbelleği hiçbir zaman doldurmaz — dolayısıyla önbellekli yol yalnızca yapısal olarak tek biçimli verileri görür.

## Yerleşik hızlı yollar

En büyük performans kazanımları, VM'ye belirli yerleşikleri öğreterek elde edildi:

- **`Atomics.load` / `store` / `compareExchange`**, `loadSpinHook` / `storeNotifyHook` işbirlikçi zamanlamayla — 4 Atomics spin döngüsü testini yeniden çalışır hâle getirdi.
- DST önbelleği için doğrudan hashmap kullanan **`Date.prototype.getTimezoneOffset`** — SpiderMonkey DST stresinde %45 hızlanma.
- Satır içi ASCII hızlı yolu kullanan **`String.fromCharCode`** — tipik bir taramada yaklaşık 1 M dağıtım tasarrufu.
- Toplu `hex2bin` çözümleme kullanan, 12 karakterlik ultra hızlı `%XX%XX%XX%XX` yolu bulunan **`decodeURI` / `decodeURIComponent`**.
- Doğrudan özellik haritası erişimi kullanan **`Object.keys` / `Object.values` / `Object.entries`**.
- Alttaki PHP dizisi üzerinde yerel yineleme kullanan **`Array.prototype.map` / `filter` / `forEach` / `reduce`**.

Her hızlı yol bir "şekil denetimi" ile korunur: alıcı beklenen şekle uymuyorsa (örneğin düz bir PHP `string` değeriyle desteklenmeyen bir TypedArray görünümü), VM belirtime uygun yavaş yola geçer.

## Bu özellik ne yapmaz

Bayt kodu VM'si, V8 anlamında **bir JIT değildir**. İşlev başına yerel makine kodu üretmez ve satır içi önbelleğin tek biçimli prototip kimliği denetiminin ötesinde, gözlemlenen türlere göre spekülasyon yapmaz. İşlev sınırlarını aşan satır içi yerleştirme, çağrılan işlevin gövdesini çağıranın bayt koduna indirgemek yerine, özel çağrı yığınıyla (her JS çağrısı için fazladan PHP çerçevesi oluşturmadan) yaklaşık olarak sağlanır.

JsToPhp aktarıcısı, motorun JIT'e en çok yaklaşan parçasıdır: sık kullanılan işlev gövdeleri için PHP kaynak kodu üretir; ardından PHP 8.5'in izleme JIT'i bunu yerel koda derler. JsToPhp, `eval()` üzerinden çalışır; bu nedenle bir defalık derleme maliyetine katlanır ve izleme JIT'i işlevi kullanıcı koduymuş gibi keşfetmelidir. Doğrudan opcache işlem kodları üreterek `eval()` adımını atlamak aradaki farkı daha da kapatır — bkz. [Kıyaslamalar § Yol Haritası](https://phasis.inline0.dev/tr/docs/advanced/benchmarks#roadmap).

## Kıyaslama

`bench/microbench.js`, bir düzineden biraz fazla küçük kıyaslama çalıştırır (loop-arith, loop-fib, fn-recurse, fn-deep-recurse, obj-create, obj-prop, proto-method, gen-iterate, arr-push, arr-map, str-concat, str-split-join, json-roundtrip, closure, destructure). Ortanca geçen süre:

```bash
php bench/run.php
```

Güncel sayılar her `bench` iş akışı çalıştırmasından sonra `BENCH.md` deposuna kaydedilir. `bench` ve test262 matris iş akışları elle başlatılır.
