diff --git a/docs/adr-frontend-analiz.md b/docs/adr-frontend-analiz.md index 17ece89..e564881 100644 --- a/docs/adr-frontend-analiz.md +++ b/docs/adr-frontend-analiz.md @@ -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`), + 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 | diff --git a/src/cli/commands/ir.hpp b/src/cli/commands/ir.hpp index 31b44c4..7ff1dbd 100644 --- a/src/cli/commands/ir.hpp +++ b/src/cli/commands/ir.hpp @@ -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; diff --git a/src/cli/commands/run.hpp b/src/cli/commands/run.hpp index 3d6a13a..1675d51 100644 --- a/src/cli/commands/run.hpp +++ b/src/cli/commands/run.hpp @@ -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; diff --git a/src/opt/optimization_manager.hpp b/src/opt/optimization_manager.hpp index af0c661..bf68657 100644 --- a/src/opt/optimization_manager.hpp +++ b/src/opt/optimization_manager.hpp @@ -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; }