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.
|
symbol bağı, erişilebilirlik, constness ekler). Ağacı **bozmaz**, zenginleştirir.
|
||||||
Orijinal AST hâlâ kaynak kodun tam izdüşümüdür.
|
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.**
|
2. **Optimizasyon dönüşümü iki yolla yapılabilir:**
|
||||||
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.
|
|
||||||
|
|
||||||
**Sonuç:** Hem "bellek canavarı" felsefesi korunur (orijinal AST her şeyi tutar),
|
a. **`ast` komutu için klon:** orijinal AST dokunulmadan kalır, klon üstünde
|
||||||
hem optimizasyon yapılır, hem de öncesi/sonrası ayrı ayrı incelenebilir.
|
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 → ham + annotate edilmiş AST (1+2 burada durur)
|
||||||
saqut ast file.sqt --optimized → klon, folding uygulanmış (3 var)
|
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
|
`deepClone` sembol tablosunu yeniden eşlemez (remap etmez) — klondaki
|
||||||
alıyor** ve bir **tutarlılık (coherence) problemini** atlıyordu. Düzeltme:
|
`IdentifierNode::resolvedSymbol` orijinal `Symbol` nesnelerini gösterir. Bu
|
||||||
|
**güvenlidir**, çünkü:
|
||||||
|
|
||||||
`ASTNode::clone()` "belki gerekir" değil, **merkezi ve spesifiye edilmesi
|
- `Symbol::references` bir **konum listesi** (`std::vector<SourceLocation>`),
|
||||||
zorunlu** bir bileşendir; tüm öncesi/sonrası hikâyesi ona dayanır (bkz. roadmap
|
referans sayacı değildir. Klonda bir `IdentifierNode` silindiğinde bu liste
|
||||||
Faz 4'te clone() yükseltildi).
|
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`'ı
|
Önceki versiyon "sembol tablosu klonlanır ve remap edilir" diyordu; bu hem hiç
|
||||||
orijinali değil, klonu göstermeli; yoksa yapısal doğrulama ve dönüşümler
|
implement edilmedi hem de gerekli değildi. Düzeltildi.
|
||||||
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.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -667,7 +666,7 @@ modelini birlikte zorlar — ikisi de bu yüzden ertelendi.
|
||||||
| ADR | Konu | Karar |
|
| ADR | Konu | Karar |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 006 | Frontend mimarisi | Çok-aşamalı; frontend/middle-end/backend katmanları |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --optimized: optimize edilmiş AST klonu üzerinden IR üret
|
// --optimized: constant folding + DCE yerinde uygulanır, klon yok.
|
||||||
ASTNode* activeAst = ast;
|
// IR dump için tek versiyon yeterli — ast komutu gibi karşılaştırma yok.
|
||||||
ASTNode* optimizedAst = nullptr;
|
|
||||||
if (args.optimized) {
|
if (args.optimized) {
|
||||||
CompilerConfig cfg;
|
CompilerConfig cfg;
|
||||||
DiagnosticEngine optDiag;
|
DiagnosticEngine optDiag;
|
||||||
OptimizationManager mgr(cfg, optDiag);
|
OptimizationManager(cfg, optDiag).runPassesInPlace(ast, &symbolTable);
|
||||||
optimizedAst = mgr.optimize(ast, &symbolTable);
|
|
||||||
activeAst = optimizedAst;
|
|
||||||
if (optDiag.errorCount() + optDiag.warningCount() > 0)
|
if (optDiag.errorCount() + optDiag.warningCount() > 0)
|
||||||
optDiag.printAll(std::cerr); // W002 vb. uyarılar stderr'e
|
optDiag.printAll(std::cerr); // W002 vb. uyarılar stderr'e
|
||||||
}
|
}
|
||||||
|
|
||||||
IRGenerator irGenerator;
|
IRGenerator irGenerator;
|
||||||
IRProgram program = irGenerator.generate(activeAst, symbolTable);
|
IRProgram program = irGenerator.generate(ast, symbolTable);
|
||||||
program.dump();
|
program.dump();
|
||||||
|
|
||||||
delete optimizedAst; // nullptr ise no-op
|
|
||||||
delete ast;
|
delete ast;
|
||||||
for (auto* t : tokens) delete t;
|
for (auto* t : tokens) delete t;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
// Tam derleme + çalıştırma pipeline'ı:
|
// Tam derleme + çalıştırma pipeline'ı:
|
||||||
// tokenize → parse → sembol topla → [opsiyonel: optimize] → IR üret → VM çalıştır
|
// tokenize → parse → sembol topla → [opsiyonel: optimize] → IR üret → VM çalıştır
|
||||||
//
|
//
|
||||||
// --optimized bayrağı: orijinal AST klonlanır, constant folding + DCE uygulanır,
|
// --optimized bayrağı: AST yerinde optimize edilir (klon yok — sadece tek versiyon
|
||||||
// optimize edilmiş klon IR generator'a verilir. Orijinal AST dokunulmadan kalır.
|
// gerekiyor). ast komutu orijinali saklaması gerektiği için klon kullanır; run/ir
|
||||||
// Aynı pattern ir.hpp'de de kullanılıyor — paralel değişikliklerde ikisine bak.
|
// kullanmaz. Aynı pattern ir.hpp'de de var — paralel değişikliklerde ikisine bak.
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
#ifndef SAQUT_CLI_RUN
|
#ifndef SAQUT_CLI_RUN
|
||||||
|
|
@ -61,24 +61,19 @@ inline int cmdRun(const CliArgs& args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Aşama 4 (opsiyonel): Optimizasyon ────────────────────────────────
|
// ── Aşama 4 (opsiyonel): Optimizasyon ────────────────────────────────
|
||||||
// --optimized bayrağı verilmişse: orijinal AST'yi kopyala, klon üstünde
|
// --optimized: constant folding + DCE yerinde uygulanır, klon yok.
|
||||||
// constant folding + DCE uygula. IR generator klonu kullanır; orijinal
|
// Tek versiyon (optimize edilmiş) yeterli — ast komutu gibi karşılaştırma yok.
|
||||||
// bu scope'ta silinir. Bayrak yoksa sıfır maliyet — klonlama olmaz.
|
|
||||||
ASTNode* activeAst = ast;
|
|
||||||
ASTNode* optimizedAst = nullptr;
|
|
||||||
if (args.optimized) {
|
if (args.optimized) {
|
||||||
CompilerConfig cfg;
|
CompilerConfig cfg;
|
||||||
DiagnosticEngine optDiag;
|
DiagnosticEngine optDiag;
|
||||||
OptimizationManager mgr(cfg, optDiag);
|
OptimizationManager(cfg, optDiag).runPassesInPlace(ast, &symbolTable);
|
||||||
optimizedAst = mgr.optimize(ast, &symbolTable);
|
|
||||||
activeAst = optimizedAst;
|
|
||||||
if (optDiag.errorCount() + optDiag.warningCount() > 0)
|
if (optDiag.errorCount() + optDiag.warningCount() > 0)
|
||||||
optDiag.printAll(std::cerr); // W002 (derleme zamanı sıfıra bölme) vb.
|
optDiag.printAll(std::cerr); // W002 (derleme zamanı sıfıra bölme) vb.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Aşama 5: IR üretimi ───────────────────────────────────────────────
|
// ── Aşama 5: IR üretimi ───────────────────────────────────────────────
|
||||||
IRGenerator irGenerator;
|
IRGenerator irGenerator;
|
||||||
IRProgram program = irGenerator.generate(activeAst, symbolTable);
|
IRProgram program = irGenerator.generate(ast, symbolTable);
|
||||||
|
|
||||||
// ── Aşama 6: VM çalıştırma ────────────────────────────────────────────
|
// ── Aşama 6: VM çalıştırma ────────────────────────────────────────────
|
||||||
int exitCode = 0;
|
int exitCode = 0;
|
||||||
|
|
@ -90,7 +85,6 @@ inline int cmdRun(const CliArgs& args) {
|
||||||
exitCode = 1;
|
exitCode = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete optimizedAst; // nullptr ise no-op; orijinal ast her durumda aşağıda silinir
|
|
||||||
delete ast;
|
delete ast;
|
||||||
for (auto* t : tokens) delete t;
|
for (auto* t : tokens) delete t;
|
||||||
return exitCode;
|
return exitCode;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,18 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// saQut — Optimizasyon Yöneticisi (ADR-007, ADR-009)
|
// saQut — Optimizasyon Yöneticisi (ADR-007, ADR-009)
|
||||||
//
|
//
|
||||||
// 1. AST'yi klonlar (orijinal dokunulmaz).
|
// İKİ KULLANIM YOLU:
|
||||||
// 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).
|
// 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
|
// 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
|
// (katlama: n düğüm → 1 düğüm; DCE: düğüm siler). Büyüten pass
|
||||||
|
|
@ -33,18 +42,23 @@ public:
|
||||||
maxRounds_ = cfg.maxFixpointRounds;
|
maxRounds_ = cfg.maxFixpointRounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
// optimize: AST'yi klonlar ve optimize edilmiş kopyayı döndürür.
|
// Pass'leri verilen AST üstünde yerinde çalıştırır — klon yok.
|
||||||
// Dönen pointer caller'a aittir (delete edilmeli).
|
// run / ir ve diğer tek-versiyon komutları bu yolu kullanır.
|
||||||
ASTNode* optimize(ASTNode* root, SymbolTable* table) {
|
void runPassesInPlace(ASTNode* root, SymbolTable* table) {
|
||||||
ASTNode* clone = deepClone(root);
|
|
||||||
|
|
||||||
for (int round = 0; round < maxRounds_; ++round) {
|
for (int round = 0; round < maxRounds_; ++round) {
|
||||||
bool anyChange = false;
|
bool anyChange = false;
|
||||||
for (auto& pass : passes_)
|
for (auto& pass : passes_)
|
||||||
if (pass->run(clone, table)) anyChange = true;
|
if (pass->run(root, table)) anyChange = true;
|
||||||
if (!anyChange) break;
|
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;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue