refactor(opt): klon sadece ast komutunda — run/ir yerinde optimize eder
ADR-007 düzeltmesi: klon yalnızca ast komutunun öncesi/sonrası karşılaştırması için gerekli; diğer komutlar tek versiyon üretiyor, klona gerek yok. - OptimizationManager: runPassesInPlace() (klon yok) + optimize() (klon, sadece ast) - run.hpp + ir.hpp: optimize() → runPassesInPlace(); activeAst/optimizedAst dansı kalktı - ast.hpp: değişmedi — hâlâ optimize() (klon) kullanıyor - ADR-007: "sembol tablosu remap edilir" yanlış kararı düzeltildi; Symbol::references konum listesi, refcount değil — paylaşım güvenli 11/11 ctest geçti
This commit is contained in:
parent
ab47e42954
commit
98bf8385d2
|
|
@ -105,46 +105,45 @@ sembol tablosunun optimizasyondan önceki ve sonraki halini ayrı ayrı görebil
|
|||
symbol bağı, erişilebilirlik, constness ekler). Ağacı **bozmaz**, zenginleştirir.
|
||||
Orijinal AST hâlâ kaynak kodun tam izdüşümüdür.
|
||||
|
||||
2. **Optimizasyon dönüşümü, ağacın bir KOPYASI (klon) üzerinde yapılır.**
|
||||
Orijinal analizli AST = "öncesi"; klon + dönüştürülmüş AST = "sonrası".
|
||||
Ağaç klonlamak ucuz ve basittir, yalnızca `--optimized` istendiğinde yapılır.
|
||||
2. **Optimizasyon dönüşümü iki yolla yapılabilir:**
|
||||
|
||||
**Sonuç:** Hem "bellek canavarı" felsefesi korunur (orijinal AST her şeyi tutar),
|
||||
hem optimizasyon yapılır, hem de öncesi/sonrası ayrı ayrı incelenebilir.
|
||||
a. **`ast` komutu için klon:** orijinal AST dokunulmadan kalır, klon üstünde
|
||||
pass'ler çalışır. Kullanıcı "öncesi" ve "sonrası" AST'yi ayrı ayrı
|
||||
görebilir. `OptimizationManager::optimize()` bu yolu kullanır.
|
||||
|
||||
b. **Diğer tüm komutlar için yerinde (in-place):** `run --optimized`,
|
||||
`ir --optimized` vb. tek versiyon üretiyor — orijinali saklamaya gerek yok.
|
||||
`OptimizationManager::runPassesInPlace()` bu yolu kullanır, klon maliyeti yok.
|
||||
|
||||
**Sonuç:** "bellek canavarı" felsefesi `ast` komutunda korunur; diğer komutlar
|
||||
gereksiz klon maliyeti taşımaz.
|
||||
|
||||
```
|
||||
saqut ast file.sqt → ham + annotate edilmiş AST (1+2 burada durur)
|
||||
saqut ast file.sqt --optimized → klon, folding uygulanmış (3 var)
|
||||
saqut run file.sqt --optimized → yerinde optimize → IR → VM (klon yok)
|
||||
saqut ir file.sqt --optimized → yerinde optimize → IR dump (klon yok)
|
||||
```
|
||||
|
||||
### Güncelleme — Klon maliyeti yük taşır (load-bearing)
|
||||
### Güncelleme — Klon ve sembol tablosu paylaşımı
|
||||
|
||||
İlk metin "ağaç klonlamak ucuz ve basittir" diyordu; bu **klon maliyetini hafife
|
||||
alıyor** ve bir **tutarlılık (coherence) problemini** atlıyordu. Düzeltme:
|
||||
`deepClone` sembol tablosunu yeniden eşlemez (remap etmez) — klondaki
|
||||
`IdentifierNode::resolvedSymbol` orijinal `Symbol` nesnelerini gösterir. Bu
|
||||
**güvenlidir**, çünkü:
|
||||
|
||||
`ASTNode::clone()` "belki gerekir" değil, **merkezi ve spesifiye edilmesi
|
||||
zorunlu** bir bileşendir; tüm öncesi/sonrası hikâyesi ona dayanır (bkz. roadmap
|
||||
Faz 4'te clone() yükseltildi).
|
||||
- `Symbol::references` bir **konum listesi** (`std::vector<SourceLocation>`),
|
||||
referans sayacı değildir. Klonda bir `IdentifierNode` silindiğinde bu liste
|
||||
değişmez.
|
||||
- `IdentifierNode` destructor'ı yoktur; `resolvedSymbol`'e dokunan hiçbir yıkıcı
|
||||
kodu çalışmaz.
|
||||
- Klondaki pass'ler Symbol nesnelerini **okur** (slot numarası, tip vb.),
|
||||
**yazmaz** — paylaşım salt-okunur (read-only) kullanımdır.
|
||||
|
||||
**Klonlanırken karar verilmesi gereken iki nokta (açıkça belgele):**
|
||||
**Parent pointer'lar** ise yeniden bağlanır — klon node'larının `parent`'ı
|
||||
orijinali değil, klonu gösterir (deepClone bunu zaten yapar).
|
||||
|
||||
1. **Parent pointer'lar yeniden bağlanmalı.** Klon node'larının `parent`'ı
|
||||
orijinali değil, klonu göstermeli; yoksa yapısal doğrulama ve dönüşümler
|
||||
yanlış ağaçta gezinir.
|
||||
|
||||
2. **`IdentifierNode → Symbol` bağları: paylaş mı, yeniden eşle mi?**
|
||||
- **Paylaş** (klon ve orijinal aynı sembol tablosuna işaret eder): ucuz, ama
|
||||
klonu optimize etmek orijinalin **referans sayımlarını bozar** (DCE klonda
|
||||
bir kullanımı silince orijinalin Symbol ref-count'u da düşer).
|
||||
- **Yeniden eşle** (klona ait bir sembol tablosu kopyası): doğru, ama ucuz
|
||||
değil.
|
||||
- **Karar:** `--optimized` istendiğinde sembol tablosu da **klonlanır ve
|
||||
yeniden eşlenir** (remap). Doğruluk, ucuzluğa tercih edilir; klon zaten
|
||||
yalnızca optimizasyon istendiğinde üretilir, sıcak yol değildir. "Ucuz"
|
||||
iddiası kaldırıldı.
|
||||
|
||||
Bu, ADR-013'teki "ref-count Symbol'da yaşar" kararıyla tutarlıdır: ref-count
|
||||
Symbol'da olduğu için, klonun kendi Symbol'larına sahip olması şarttır.
|
||||
Önceki versiyon "sembol tablosu klonlanır ve remap edilir" diyordu; bu hem hiç
|
||||
implement edilmedi hem de gerekli değildi. Düzeltildi.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -667,7 +666,7 @@ modelini birlikte zorlar — ikisi de bu yüzden ertelendi.
|
|||
| ADR | Konu | Karar |
|
||||
|---|---|---|
|
||||
| 006 | Frontend mimarisi | Çok-aşamalı; frontend/middle-end/backend katmanları |
|
||||
| 007 | Analiz vs optimizasyon | Analiz yerinde işaretler; optimizasyon klonda dönüştürür; `clone()` merkezi, sembol tablosu remap edilir |
|
||||
| 007 | Analiz vs optimizasyon | Analiz yerinde; `ast` komutu klon üstünde dönüştürür (öncesi/sonrası karşılaştırması); `run`/`ir` yerinde optimize eder (klon yok); sembol bağları salt-okunur paylaşım (remap gerekmez) |
|
||||
| 008 | Optimizasyon konumu | Basitler AST'de, dataflow gerektirenler IR'de |
|
||||
| 009 | Pass yönetimi | Fixpoint döngüsü, toggle'lı; monotonluk/iterasyon-tavanı değişmezi; akışa-bağlı analiz tur başına tazelenir |
|
||||
| 010 | Tip sistemi | Minimal+genişletilebilir Type; gizli dönüşüm yok; Error tipi; tamsayı literali bağlama-göre tiplenir |
|
||||
|
|
|
|||
|
|
@ -43,24 +43,20 @@ inline int cmdIr(const CliArgs& args) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// --optimized: optimize edilmiş AST klonu üzerinden IR üret
|
||||
ASTNode* activeAst = ast;
|
||||
ASTNode* optimizedAst = nullptr;
|
||||
// --optimized: constant folding + DCE yerinde uygulanır, klon yok.
|
||||
// IR dump için tek versiyon yeterli — ast komutu gibi karşılaştırma yok.
|
||||
if (args.optimized) {
|
||||
CompilerConfig cfg;
|
||||
DiagnosticEngine optDiag;
|
||||
OptimizationManager mgr(cfg, optDiag);
|
||||
optimizedAst = mgr.optimize(ast, &symbolTable);
|
||||
activeAst = optimizedAst;
|
||||
OptimizationManager(cfg, optDiag).runPassesInPlace(ast, &symbolTable);
|
||||
if (optDiag.errorCount() + optDiag.warningCount() > 0)
|
||||
optDiag.printAll(std::cerr); // W002 vb. uyarılar stderr'e
|
||||
}
|
||||
|
||||
IRGenerator irGenerator;
|
||||
IRProgram program = irGenerator.generate(activeAst, symbolTable);
|
||||
IRProgram program = irGenerator.generate(ast, symbolTable);
|
||||
program.dump();
|
||||
|
||||
delete optimizedAst; // nullptr ise no-op
|
||||
delete ast;
|
||||
for (auto* t : tokens) delete t;
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
// Tam derleme + çalıştırma pipeline'ı:
|
||||
// tokenize → parse → sembol topla → [opsiyonel: optimize] → IR üret → VM çalıştır
|
||||
//
|
||||
// --optimized bayrağı: orijinal AST klonlanır, constant folding + DCE uygulanır,
|
||||
// optimize edilmiş klon IR generator'a verilir. Orijinal AST dokunulmadan kalır.
|
||||
// Aynı pattern ir.hpp'de de kullanılıyor — paralel değişikliklerde ikisine bak.
|
||||
// --optimized bayrağı: AST yerinde optimize edilir (klon yok — sadece tek versiyon
|
||||
// gerekiyor). ast komutu orijinali saklaması gerektiği için klon kullanır; run/ir
|
||||
// kullanmaz. Aynı pattern ir.hpp'de de var — paralel değişikliklerde ikisine bak.
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_CLI_RUN
|
||||
|
|
@ -61,24 +61,19 @@ inline int cmdRun(const CliArgs& args) {
|
|||
}
|
||||
|
||||
// ── Aşama 4 (opsiyonel): Optimizasyon ────────────────────────────────
|
||||
// --optimized bayrağı verilmişse: orijinal AST'yi kopyala, klon üstünde
|
||||
// constant folding + DCE uygula. IR generator klonu kullanır; orijinal
|
||||
// bu scope'ta silinir. Bayrak yoksa sıfır maliyet — klonlama olmaz.
|
||||
ASTNode* activeAst = ast;
|
||||
ASTNode* optimizedAst = nullptr;
|
||||
// --optimized: constant folding + DCE yerinde uygulanır, klon yok.
|
||||
// Tek versiyon (optimize edilmiş) yeterli — ast komutu gibi karşılaştırma yok.
|
||||
if (args.optimized) {
|
||||
CompilerConfig cfg;
|
||||
DiagnosticEngine optDiag;
|
||||
OptimizationManager mgr(cfg, optDiag);
|
||||
optimizedAst = mgr.optimize(ast, &symbolTable);
|
||||
activeAst = optimizedAst;
|
||||
OptimizationManager(cfg, optDiag).runPassesInPlace(ast, &symbolTable);
|
||||
if (optDiag.errorCount() + optDiag.warningCount() > 0)
|
||||
optDiag.printAll(std::cerr); // W002 (derleme zamanı sıfıra bölme) vb.
|
||||
}
|
||||
|
||||
// ── Aşama 5: IR üretimi ───────────────────────────────────────────────
|
||||
IRGenerator irGenerator;
|
||||
IRProgram program = irGenerator.generate(activeAst, symbolTable);
|
||||
IRProgram program = irGenerator.generate(ast, symbolTable);
|
||||
|
||||
// ── Aşama 6: VM çalıştırma ────────────────────────────────────────────
|
||||
int exitCode = 0;
|
||||
|
|
@ -90,7 +85,6 @@ inline int cmdRun(const CliArgs& args) {
|
|||
exitCode = 1;
|
||||
}
|
||||
|
||||
delete optimizedAst; // nullptr ise no-op; orijinal ast her durumda aşağıda silinir
|
||||
delete ast;
|
||||
for (auto* t : tokens) delete t;
|
||||
return exitCode;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,18 @@
|
|||
// ============================================================================
|
||||
// saQut — Optimizasyon Yöneticisi (ADR-007, ADR-009)
|
||||
//
|
||||
// 1. AST'yi klonlar (orijinal dokunulmaz).
|
||||
// 2. Etkin pass'leri fixpoint döngüsüyle çalıştırır.
|
||||
// 3. Optimize edilmiş klon sahipliğini döndürür (caller delete eder).
|
||||
// İKİ KULLANIM YOLU:
|
||||
//
|
||||
// 1. runPassesInPlace(root, table)
|
||||
// Pass'leri verilen AST üstünde doğrudan çalıştırır — klon yok.
|
||||
// run / ir / transpile gibi "tek seferlik" komutlar bu yolu kullanır:
|
||||
// AST'nin tek versiyonu gerekiyor, orijinali saklamaya gerek yok.
|
||||
//
|
||||
// 2. optimize(root, table) [sadece ast komutu için]
|
||||
// Önce deepClone, sonra runPassesInPlace. Orijinali dokunulmaz bırakır.
|
||||
// Çıktı: optimize edilmiş klon (caller delete eder).
|
||||
// ast komutunun "öncesi / sonrası" karşılaştırması için zorunlu.
|
||||
// Diğer komutlar bu yolu ÇAĞIRMAMALI — gereksiz klon maliyeti.
|
||||
//
|
||||
// Fixpoint garantisi: her pass yalnızca küçülten dönüşümler yapar
|
||||
// (katlama: n düğüm → 1 düğüm; DCE: düğüm siler). Büyüten pass
|
||||
|
|
@ -33,18 +42,23 @@ public:
|
|||
maxRounds_ = cfg.maxFixpointRounds;
|
||||
}
|
||||
|
||||
// optimize: AST'yi klonlar ve optimize edilmiş kopyayı döndürür.
|
||||
// Dönen pointer caller'a aittir (delete edilmeli).
|
||||
ASTNode* optimize(ASTNode* root, SymbolTable* table) {
|
||||
ASTNode* clone = deepClone(root);
|
||||
|
||||
// Pass'leri verilen AST üstünde yerinde çalıştırır — klon yok.
|
||||
// run / ir ve diğer tek-versiyon komutları bu yolu kullanır.
|
||||
void runPassesInPlace(ASTNode* root, SymbolTable* table) {
|
||||
for (int round = 0; round < maxRounds_; ++round) {
|
||||
bool anyChange = false;
|
||||
for (auto& pass : passes_)
|
||||
if (pass->run(clone, table)) anyChange = true;
|
||||
if (pass->run(root, table)) anyChange = true;
|
||||
if (!anyChange) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Önce deepClone, sonra runPassesInPlace. Orijinal dokunulmaz.
|
||||
// SADECE ast komutu kullanır — öncesi/sonrası karşılaştırması için.
|
||||
// Dönen pointer caller'a aittir (delete edilmeli).
|
||||
ASTNode* optimize(ASTNode* root, SymbolTable* table) {
|
||||
ASTNode* clone = deepClone(root);
|
||||
runPassesInPlace(clone, table);
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue